dsh-pstack 0.2.0-beta.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/LICENSE +2 -3
  2. package/README.md +148 -84
  3. package/README.zh.md +198 -0
  4. package/assets/pstack-config-entry.png +0 -0
  5. package/cordis.patch.yml +105 -27
  6. package/dist/client.mjs +2341 -0
  7. package/dist/client.mjs.map +1 -0
  8. package/dist/host-contract-CUPSpeNk.d.mts +22 -0
  9. package/dist/index.d.mts +49 -238
  10. package/dist/index.mjs +250 -123
  11. package/dist/models.d.mts +44 -0
  12. package/dist/models.mjs +201 -0
  13. package/dist/paths-BT77khwm.mjs +48 -0
  14. package/dist/pstack/LICENSE +21 -0
  15. package/dist/pstack/README.md +291 -0
  16. package/dist/pstack/agents/poteto-agent.md +9 -0
  17. package/{assets → dist/pstack}/docs/guide/01-setup.md +13 -11
  18. package/{assets → dist/pstack}/docs/guide/02-poteto-mode.md +4 -4
  19. package/{assets → dist/pstack}/docs/guide/04-design.md +4 -4
  20. package/{assets → dist/pstack}/docs/guide/05-build-and-clean.md +2 -2
  21. package/{assets → dist/pstack}/docs/guide/06-verify-and-ship.md +13 -3
  22. package/{assets → dist/pstack}/docs/guide/07-overnight.md +15 -13
  23. package/{assets → dist/pstack}/docs/guide/09-make-it-yours.md +6 -2
  24. package/{assets → dist/pstack}/docs/guide/10-recipes-and-pitfalls.md +7 -5
  25. package/{assets → dist/pstack}/docs/guide/README.md +1 -1
  26. package/dist/pstack/docs/harness/dsh.md +98 -0
  27. package/{assets → dist/pstack}/skills/architect/SKILL.md +12 -12
  28. package/dist/pstack/skills/arena/SKILL.md +50 -0
  29. package/{assets → dist/pstack}/skills/automate-me/SKILL.md +14 -14
  30. package/{assets → dist/pstack}/skills/benchmark-checklist/SKILL.md +1 -1
  31. package/{assets → dist/pstack}/skills/blast-radius/SKILL.md +4 -6
  32. package/{assets → dist/pstack}/skills/correct/SKILL.md +1 -1
  33. package/{assets → dist/pstack}/skills/create-formal-verification-skill/SKILL.md +4 -2
  34. package/{assets → dist/pstack}/skills/create-verification-skill/SKILL.md +5 -5
  35. package/dist/pstack/skills/figure-it-out/SKILL.md +54 -0
  36. package/dist/pstack/skills/how/SKILL.md +38 -0
  37. package/{assets → dist/pstack}/skills/in-your-own-words/SKILL.md +1 -1
  38. package/dist/pstack/skills/interrogate/SKILL.md +33 -0
  39. package/{assets → dist/pstack}/skills/ketchup/SKILL.md +3 -5
  40. package/dist/pstack/skills/ketchup-video/SKILL.md +29 -0
  41. package/{assets → dist/pstack}/skills/maintain-formal-verification-skill/SKILL.md +1 -1
  42. package/{assets → dist/pstack}/skills/maintain-verification-skill/SKILL.md +3 -3
  43. package/dist/pstack/skills/make-bot-ui/SKILL.md +117 -0
  44. package/dist/pstack/skills/no-comments/SKILL.md +26 -0
  45. package/{assets → dist/pstack}/skills/poteto-help/SKILL.md +34 -31
  46. package/{assets → dist/pstack}/skills/poteto-help/references/prompting.md +1 -1
  47. package/{assets → dist/pstack}/skills/poteto-help/references/recipes.md +3 -3
  48. package/{assets → dist/pstack}/skills/poteto-mode/SKILL.md +20 -19
  49. package/dist/pstack/skills/poteto-mode/playbooks/authoring-a-skill.md +12 -0
  50. package/dist/pstack/skills/poteto-mode/playbooks/autonomous-run.md +13 -0
  51. package/dist/pstack/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
  52. package/dist/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +8 -0
  53. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/babysit.md +4 -4
  54. package/dist/pstack/skills/poteto-mode/playbooks/bug-fix.md +15 -0
  55. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/eval.md +3 -3
  56. package/dist/pstack/skills/poteto-mode/playbooks/feature.md +21 -0
  57. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/hillclimb.md +3 -3
  58. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/investigation.md +2 -2
  59. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/multi-phase-plan.md +23 -23
  60. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/opening-a-pr.md +6 -6
  61. package/dist/pstack/skills/poteto-mode/playbooks/orchestrate.md +25 -0
  62. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/pause-safely.md +2 -2
  63. package/dist/pstack/skills/poteto-mode/playbooks/perf-issue.md +25 -0
  64. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/prototype.md +4 -4
  65. package/dist/pstack/skills/poteto-mode/playbooks/refactoring.md +16 -0
  66. package/dist/pstack/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
  67. package/dist/pstack/skills/poteto-mode/playbooks/session-pickup.md +11 -0
  68. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/shipping.md +2 -2
  69. package/{assets → dist/pstack}/skills/poteto-mode/playbooks/trace-forensics.md +2 -2
  70. package/dist/pstack/skills/poteto-mode/playbooks/visual-parity.md +11 -0
  71. package/dist/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md +12 -0
  72. package/{assets → dist/pstack}/skills/poteto-mode/scripts/check-plan.mjs +6 -8
  73. package/dist/pstack/skills/poteto-mode/scripts/check-plan.test.mjs +128 -0
  74. package/{assets/skills/poteto-mode/scripts/check-plan.test.mjs → dist/pstack/skills/poteto-mode/scripts/expect.mjs} +12 -101
  75. package/dist/pstack/skills/poteto-mode/scripts/support.mjs +93 -0
  76. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.mjs +123 -0
  77. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +217 -0
  78. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.mjs +119 -0
  79. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.mjs +626 -0
  80. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/github.test.mjs +521 -0
  81. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.mjs +657 -0
  82. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +523 -0
  83. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/render.mjs +106 -0
  84. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/types.mjs +9 -0
  85. package/dist/pstack/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +5 -0
  86. package/{assets → dist/pstack}/skills/poteto-mode/scripts/worktree-audit.mjs +15 -22
  87. package/dist/pstack/skills/poteto-mode/scripts/worktree-audit.test.mjs +134 -0
  88. package/{assets → dist/pstack}/skills/principle-attack-the-premise/SKILL.md +1 -1
  89. package/{assets → dist/pstack}/skills/principle-boundary-discipline/SKILL.md +1 -1
  90. package/{assets → dist/pstack}/skills/principle-encode-lessons-in-structure/SKILL.md +1 -1
  91. package/{assets → dist/pstack}/skills/principle-exhaust-the-design-space/SKILL.md +1 -1
  92. package/{assets → dist/pstack}/skills/principle-experience-first/SKILL.md +1 -1
  93. package/{assets → dist/pstack}/skills/principle-explain-the-number/SKILL.md +3 -2
  94. package/{assets → dist/pstack}/skills/principle-fix-root-causes/SKILL.md +1 -1
  95. package/{assets → dist/pstack}/skills/principle-laziness-protocol/SKILL.md +1 -1
  96. package/{assets → dist/pstack}/skills/principle-make-operations-idempotent/SKILL.md +1 -1
  97. package/{assets → dist/pstack}/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +1 -1
  98. package/{assets → dist/pstack}/skills/principle-minimize-reader-load/SKILL.md +1 -1
  99. package/{assets → dist/pstack}/skills/principle-model-the-domain/SKILL.md +1 -1
  100. package/{assets → dist/pstack}/skills/principle-never-block-on-the-human/SKILL.md +1 -1
  101. package/{assets → dist/pstack}/skills/principle-outcome-oriented-execution/SKILL.md +1 -1
  102. package/{assets → dist/pstack}/skills/principle-prove-it-works/SKILL.md +2 -2
  103. package/{assets → dist/pstack}/skills/principle-redesign-from-first-principles/SKILL.md +1 -1
  104. package/{assets → dist/pstack}/skills/principle-separate-before-serializing-shared-state/SKILL.md +1 -1
  105. package/{assets → dist/pstack}/skills/principle-sequence-verifiable-units/SKILL.md +1 -1
  106. package/{assets → dist/pstack}/skills/principle-subtract-before-you-add/SKILL.md +1 -1
  107. package/{assets → dist/pstack}/skills/principle-test-behavior-not-implementation/SKILL.md +1 -1
  108. package/{assets → dist/pstack}/skills/principle-type-system-discipline/SKILL.md +1 -1
  109. package/dist/pstack/skills/recall/SKILL.md +37 -0
  110. package/dist/pstack/skills/reflect/SKILL.md +71 -0
  111. package/{assets → dist/pstack}/skills/reflect/references/divergent-reviewer.md +4 -4
  112. package/{assets → dist/pstack}/skills/reflect/references/judgment-reviewer.md +2 -2
  113. package/{assets → dist/pstack}/skills/reflect/references/synthesizer.md +5 -5
  114. package/{assets → dist/pstack}/skills/reflect/references/tooling-reviewer.md +2 -2
  115. package/dist/pstack/skills/setup-pstack/SKILL.md +80 -0
  116. package/{assets → dist/pstack}/skills/sharpen/SKILL.md +1 -1
  117. package/{assets → dist/pstack}/skills/show-me-your-work/SKILL.md +8 -8
  118. package/{assets → dist/pstack}/skills/show-me-your-work/scripts/log.mjs +1 -3
  119. package/dist/pstack/skills/show-me-your-work/scripts/log.test.mjs +56 -0
  120. package/dist/pstack/skills/statechart/SKILL.md +82 -0
  121. package/dist/pstack/skills/statechart/references/render.md +93 -0
  122. package/dist/pstack/skills/statechart/scripts/backends/cfg.mjs +31 -0
  123. package/dist/pstack/skills/statechart/scripts/backends/mermaid.mjs +186 -0
  124. package/dist/pstack/skills/statechart/scripts/backends/plain-text.mjs +62 -0
  125. package/dist/pstack/skills/statechart/scripts/backends/report.mjs +84 -0
  126. package/dist/pstack/skills/statechart/scripts/frontends/config.mjs +268 -0
  127. package/dist/pstack/skills/statechart/scripts/frontends/dot.mjs +67 -0
  128. package/dist/pstack/skills/statechart/scripts/frontends/marks.mjs +94 -0
  129. package/dist/pstack/skills/statechart/scripts/frontends/messages.mjs +48 -0
  130. package/dist/pstack/skills/statechart/scripts/frontends/spec.mjs +101 -0
  131. package/dist/pstack/skills/statechart/scripts/frontends/trace.mjs +39 -0
  132. package/dist/pstack/skills/statechart/scripts/frontends/value.mjs +180 -0
  133. package/dist/pstack/skills/statechart/scripts/frontends/verdict.mjs +40 -0
  134. package/dist/pstack/skills/statechart/scripts/frontends/view.mjs +99 -0
  135. package/dist/pstack/skills/statechart/scripts/ir.mjs +277 -0
  136. package/dist/pstack/skills/statechart/scripts/passes/annotate.mjs +92 -0
  137. package/dist/pstack/skills/statechart/scripts/passes/label.mjs +210 -0
  138. package/dist/pstack/skills/statechart/scripts/passes/lift.mjs +84 -0
  139. package/dist/pstack/skills/statechart/scripts/passes/loop-check.mjs +47 -0
  140. package/dist/pstack/skills/statechart/scripts/passes/paths.mjs +72 -0
  141. package/dist/pstack/skills/statechart/scripts/passes/reach.mjs +135 -0
  142. package/dist/pstack/skills/statechart/scripts/passes/region-check.mjs +82 -0
  143. package/dist/pstack/skills/statechart/scripts/passes/regions.mjs +268 -0
  144. package/dist/pstack/skills/statechart/scripts/pipeline.mjs +72 -0
  145. package/dist/pstack/skills/statechart/scripts/run.mjs +440 -0
  146. package/dist/pstack/skills/statechart/scripts/statechart.mjs +155 -0
  147. package/dist/pstack/skills/statechart/scripts/statechart.test.mjs +198 -0
  148. package/dist/pstack/skills/statechart/scripts/worker.mjs +31 -0
  149. package/dist/pstack/skills/swarm/SKILL.md +43 -0
  150. package/{assets → dist/pstack}/skills/tdd/SKILL.md +1 -1
  151. package/{assets → dist/pstack}/skills/teach/SKILL.md +2 -2
  152. package/{assets → dist/pstack}/skills/technical-writing/SKILL.md +1 -1
  153. package/{assets → dist/pstack}/skills/typescript-best-practices/SKILL.md +3 -6
  154. package/{assets → dist/pstack}/skills/why/SKILL.md +8 -8
  155. package/dist/remote.d.mts +27 -0
  156. package/dist/remote.mjs +234 -0
  157. package/dist/role-config-NNn5r1JX.mjs +415 -0
  158. package/dist/role-manifest-C3fUzjLI.d.mts +19 -0
  159. package/dist/role-wire-DZwTn2wF.d.mts +55 -0
  160. package/dist/roles.d.mts +18 -0
  161. package/dist/roles.mjs +323 -0
  162. package/dist/script.d.mts +9 -111
  163. package/dist/script.mjs +32 -329
  164. package/dist/skill.d.mts +38 -0
  165. package/dist/skill.mjs +183 -0
  166. package/dist/skills/authored/decision-audit/SKILL.md +103 -0
  167. package/dist/skills/authored/decision-audit/SKILL.zh.md +103 -0
  168. package/dist/skills/authored/decision-audit/references/recover-commit.md +17 -0
  169. package/dist/skills/authored/decision-audit/references/recover-commit.zh.md +17 -0
  170. package/dist/skills/authored/decision-write/SKILL.md +133 -0
  171. package/dist/skills/authored/decision-write/SKILL.zh.md +133 -0
  172. package/dist/skills/authored/decision-write/references/land.md +48 -0
  173. package/dist/skills/authored/decision-write/references/land.zh.md +48 -0
  174. package/dist/skills/authored/decision-write/references/scope.md +16 -0
  175. package/dist/skills/authored/decision-write/references/scope.zh.md +16 -0
  176. package/dist/skills/authored/docs-drift/SKILL.md +120 -0
  177. package/dist/skills/authored/docs-drift/SKILL.zh.md +120 -0
  178. package/dist/skills/authored/setup-project-docs/SKILL.md +63 -0
  179. package/dist/skills/authored/setup-project-docs/SKILL.zh.md +63 -0
  180. package/dist/skills/authored/setup-project-docs/references/decision-conventions.md +13 -0
  181. package/dist/skills/authored/setup-project-docs/references/decision-conventions.zh.md +13 -0
  182. package/dist/skills/vendored/retro/SKILL.md +44 -0
  183. package/dist/skills/vendored/retro/agents/openai.yaml +5 -0
  184. package/dist/skills/vendored/writing-for-agents/SKILL-MECHANICS.md +22 -0
  185. package/dist/skills/vendored/writing-for-agents/SKILL.md +81 -0
  186. package/dist/skills/vendored/writing-for-agents/agents/openai.yaml +3 -0
  187. package/dist/subagent.d.mts +54 -206
  188. package/dist/subagent.mjs +258 -963
  189. package/locale/en.json +3 -2
  190. package/locale/zh.json +3 -2
  191. package/package.json +93 -66
  192. package/assets/agents/poteto-agent.md +0 -8
  193. package/assets/docs/harness/dsh.md +0 -60
  194. package/assets/docs/pstack-models.example.md +0 -54
  195. package/assets/skills/arena/SKILL.md +0 -50
  196. package/assets/skills/figure-it-out/SKILL.md +0 -54
  197. package/assets/skills/how/SKILL.md +0 -38
  198. package/assets/skills/interrogate/SKILL.md +0 -71
  199. package/assets/skills/interrogate/references/code-quality-review.md +0 -47
  200. package/assets/skills/interrogate/references/lead-judgment.md +0 -39
  201. package/assets/skills/interrogate/references/reviewer-prompt.md +0 -62
  202. package/assets/skills/interrogate/references/rubric.md +0 -77
  203. package/assets/skills/no-comments/SKILL.md +0 -26
  204. package/assets/skills/poteto-mode/playbooks/authoring-a-skill.md +0 -12
  205. package/assets/skills/poteto-mode/playbooks/autonomous-run.md +0 -15
  206. package/assets/skills/poteto-mode/playbooks/autopilot-full.md +0 -25
  207. package/assets/skills/poteto-mode/playbooks/autopilot-stack.md +0 -12
  208. package/assets/skills/poteto-mode/playbooks/bug-fix.md +0 -15
  209. package/assets/skills/poteto-mode/playbooks/feature.md +0 -21
  210. package/assets/skills/poteto-mode/playbooks/orchestrate.md +0 -112
  211. package/assets/skills/poteto-mode/playbooks/perf-issue.md +0 -25
  212. package/assets/skills/poteto-mode/playbooks/refactoring.md +0 -16
  213. package/assets/skills/poteto-mode/playbooks/runtime-forensics.md +0 -11
  214. package/assets/skills/poteto-mode/playbooks/session-pickup.md +0 -11
  215. package/assets/skills/poteto-mode/playbooks/visual-parity.md +0 -11
  216. package/assets/skills/poteto-mode/playbooks/worktree-cleanup.md +0 -20
  217. package/assets/skills/poteto-mode/scripts/orch/orch.mjs +0 -4282
  218. package/assets/skills/poteto-mode/scripts/orch/orch.test.mjs +0 -2042
  219. package/assets/skills/poteto-mode/scripts/watch-pr/cli.test.mjs +0 -5101
  220. package/assets/skills/poteto-mode/scripts/watch-pr/github.test.mjs +0 -1177
  221. package/assets/skills/poteto-mode/scripts/watch-pr/policy.test.mjs +0 -1441
  222. package/assets/skills/poteto-mode/scripts/watch-pr/watch-pr.mjs +0 -4601
  223. package/assets/skills/recall/SKILL.md +0 -37
  224. package/assets/skills/reflect/SKILL.md +0 -69
  225. package/assets/skills/setup-pstack/SKILL.md +0 -67
  226. package/assets/skills/swarm/SKILL.md +0 -43
  227. package/dist/frontmatter-BQX3ekwP.mjs +0 -229
  228. package/dist/frontmatter-BQX3ekwP.mjs.map +0 -1
  229. package/dist/history.d.mts +0 -429
  230. package/dist/history.mjs +0 -1129
  231. package/dist/history.mjs.map +0 -1
  232. package/dist/host-contract-o47JH_r8.d.mts +0 -527
  233. package/dist/index.mjs.map +0 -1
  234. package/dist/paths-D_JlVmBk.mjs +0 -42
  235. package/dist/paths-D_JlVmBk.mjs.map +0 -1
  236. package/dist/script.mjs.map +0 -1
  237. package/dist/subagent.mjs.map +0 -1
  238. /package/{static/icon.png → assets/logo.png} +0 -0
  239. /package/{assets → dist/pstack}/agents/comment-sicko.md +0 -0
  240. /package/{assets → dist/pstack}/docs/guide/03-understand.md +0 -0
  241. /package/{assets → dist/pstack}/docs/guide/08-principles.md +0 -0
  242. /package/{assets → dist/pstack}/docs/guide/images/design.jpg +0 -0
  243. /package/{assets → dist/pstack}/docs/guide/images/overnight.jpg +0 -0
  244. /package/{assets → dist/pstack}/docs/guide/images/recipes.jpg +0 -0
  245. /package/{assets → dist/pstack}/docs/guide/images/router.jpg +0 -0
  246. /package/{assets → dist/pstack}/docs/guide/images/understanding.jpg +0 -0
  247. /package/{assets → dist/pstack}/docs/guide/images/verification.jpg +0 -0
  248. /package/{assets → dist/pstack}/skills/architect/references/design-red-flags.md +0 -0
  249. /package/{assets → dist/pstack}/skills/architect/references/rationale-template.md +0 -0
  250. /package/{assets → dist/pstack}/skills/architect/references/runner-prompt.md +0 -0
  251. /package/{assets → dist/pstack}/skills/bro/SKILL.md +0 -0
  252. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/README.md +0 -0
  253. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/create-note.md +0 -0
  254. /package/{assets → dist/pstack}/skills/create-verification-skill/references/feature-map-example/search.md +0 -0
  255. /package/{assets → dist/pstack}/skills/how/references/explainer-prompt.md +0 -0
  256. /package/{assets → dist/pstack}/skills/how/references/explorer-prompt.md +0 -0
  257. /package/{assets → dist/pstack}/skills/poteto-mode/references/bugbot-triage.md +0 -0
  258. /package/{assets → dist/pstack}/skills/principle-build-the-lever/SKILL.md +0 -0
  259. /package/{assets → dist/pstack}/skills/principle-foundational-thinking/SKILL.md +0 -0
  260. /package/{assets → dist/pstack}/skills/principle-guard-the-context-window/SKILL.md +0 -0
  261. /package/{assets → dist/pstack}/skills/show-me-your-work/references/decision-log-template.tsv +0 -0
  262. /package/{assets → dist/pstack}/skills/typescript-best-practices/references/patterns.md +0 -0
  263. /package/{assets → dist/pstack}/skills/unslop/SKILL.md +0 -0
  264. /package/{assets → dist/pstack}/skills/why/references/epistemics.md +0 -0
  265. /package/{assets → dist/pstack}/skills/why/references/investigator-prompt.md +0 -0
  266. /package/{assets → dist/pstack}/skills/why/references/source-playbook.md +0 -0
  267. /package/{assets → dist/pstack}/skills/why/references/sources/code-archaeology.md +0 -0
  268. /package/{assets → dist/pstack}/skills/why/references/sources/databricks.md +0 -0
  269. /package/{assets → dist/pstack}/skills/why/references/sources/datadog.md +0 -0
  270. /package/{assets → dist/pstack}/skills/why/references/sources/incident-postmortem.md +0 -0
  271. /package/{assets → dist/pstack}/skills/why/references/sources/linear.md +0 -0
  272. /package/{assets → dist/pstack}/skills/why/references/sources/notion.md +0 -0
  273. /package/{assets → dist/pstack}/skills/why/references/sources/sentry.md +0 -0
  274. /package/{assets → dist/pstack}/skills/why/references/sources/slack.md +0 -0
  275. /package/{assets → dist/pstack}/skills/why/references/synthesizer-prompt.md +0 -0
@@ -0,0 +1,234 @@
1
+ // src/remote.ts
2
+ /** npm package that owns these methods. */
3
+ const REMOTE_PACKAGE = "dsh-pstack";
4
+ /** Cordis service key and wire namespace of the Host role service. */
5
+ const ROLES_SERVICE = "pstackRoles";
6
+ function jsonCodec(typeSymbol, parse, decode) {
7
+ return {
8
+ mode: "strict",
9
+ typeSymbol,
10
+ create: () => ({ parse }),
11
+ ...decode === void 0 ? {} : { decode }
12
+ };
13
+ }
14
+ function parameter(wire, typeSymbol, parse) {
15
+ return {
16
+ name: wire,
17
+ wire,
18
+ source: "json",
19
+ codec: jsonCodec(typeSymbol, parse)
20
+ };
21
+ }
22
+ function parseRecord(value, subject) {
23
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(`${subject} must be an object`);
24
+ return value;
25
+ }
26
+ function parseRoute(value, subject) {
27
+ const record = parseRecord(value, subject);
28
+ if (record["kind"] === "inherit") {
29
+ const alias = record["alias"] ?? "inherit-parent";
30
+ if (alias !== "inherit-parent" && alias !== "auto") throw new TypeError(`${subject}.alias must be "inherit-parent" or "auto"`);
31
+ return {
32
+ kind: "inherit",
33
+ alias
34
+ };
35
+ }
36
+ if (record["kind"] !== "route") throw new TypeError(`${subject}.kind is unknown`);
37
+ const provider = record["provider"];
38
+ const model = record["model"];
39
+ const effort = record["effort"];
40
+ if (typeof provider !== "string" || provider.trim() === "") throw new TypeError(`${subject}.provider must be a non-empty string`);
41
+ if (typeof model !== "string" || model.trim() === "") throw new TypeError(`${subject}.model must be a non-empty string`);
42
+ if (effort !== void 0 && (typeof effort !== "string" || effort.trim() === "")) throw new TypeError(`${subject}.effort must be a non-empty string when present`);
43
+ return {
44
+ kind: "route",
45
+ provider,
46
+ model,
47
+ ...typeof effort === "string" ? { effort } : {}
48
+ };
49
+ }
50
+ /** Parse one save request from the wire. */
51
+ function parseSaveRoleRequest(value) {
52
+ const record = parseRecord(value, "the save request");
53
+ if (typeof record["key"] !== "string") throw new TypeError("the save request must name a role");
54
+ const routes = record["routes"];
55
+ if (!Array.isArray(routes)) throw new TypeError("the save request must carry a route list");
56
+ return {
57
+ key: record["key"],
58
+ routes: routes.map((route, index) => parseRoute(route, `routes[${index}]`))
59
+ };
60
+ }
61
+ function parseRoleFileIssue(value) {
62
+ const record = parseRecord(value, "a role file issue");
63
+ if (typeof record["message"] !== "string") throw new TypeError("a role file issue needs a message");
64
+ const line = record["line"];
65
+ if (line !== void 0 && typeof line !== "number") throw new TypeError("a role file issue line must be a number");
66
+ return {
67
+ ...typeof line === "number" ? { line } : {},
68
+ message: record["message"]
69
+ };
70
+ }
71
+ function parseRoleView(value, index) {
72
+ const record = parseRecord(value, `roles[${index}]`);
73
+ if (typeof record["key"] !== "string") throw new TypeError(`roles[${index}].key must be a string`);
74
+ if (record["shape"] !== "single" && record["shape"] !== "list") throw new TypeError(`roles[${index}].shape is unknown`);
75
+ if (typeof record["configured"] !== "boolean") throw new TypeError(`roles[${index}].configured must be a boolean`);
76
+ const routes = record["routes"];
77
+ const issues = record["issues"];
78
+ if (!Array.isArray(routes) || !Array.isArray(issues)) throw new TypeError(`roles[${index}] must carry routes and issues arrays`);
79
+ const line = record["line"];
80
+ return {
81
+ key: record["key"],
82
+ shape: record["shape"],
83
+ configured: record["configured"],
84
+ ...typeof line === "number" ? { line } : {},
85
+ routes: routes.map((route, routeIndex) => parseRoute(route, `roles[${index}].routes[${routeIndex}]`)),
86
+ issues: issues.map((issue) => String(issue))
87
+ };
88
+ }
89
+ /** Parse a role-file snapshot from the wire. */
90
+ function parseRoleFileSnapshot(value) {
91
+ const record = parseRecord(value, "the role file snapshot");
92
+ const status = record["status"];
93
+ if (status !== "missing" && status !== "loaded" && status !== "unreadable") throw new TypeError("the role file snapshot status is unknown");
94
+ if (typeof record["path"] !== "string") throw new TypeError("the role file snapshot path must be a string");
95
+ if (typeof record["canSave"] !== "boolean") throw new TypeError("the role file snapshot canSave must be a boolean");
96
+ const roles = record["roles"];
97
+ const issues = record["issues"];
98
+ if (!Array.isArray(roles) || !Array.isArray(issues)) throw new TypeError("the role file snapshot must carry roles and issues arrays");
99
+ const reason = record["reason"];
100
+ const budget = record["budget"];
101
+ return {
102
+ path: record["path"],
103
+ status,
104
+ ...typeof reason === "string" ? { reason } : {},
105
+ ...typeof budget === "string" ? { budget } : {},
106
+ roles: roles.map((role, index) => parseRoleView(role, index)),
107
+ issues: issues.map(parseRoleFileIssue),
108
+ canSave: record["canSave"]
109
+ };
110
+ }
111
+ /** Parse a save result from the wire. */
112
+ function parseSaveRoleResult(value) {
113
+ const record = parseRecord(value, "the save result");
114
+ if (record["ok"] === true) return {
115
+ ok: true,
116
+ snapshot: parseRoleFileSnapshot(record["snapshot"])
117
+ };
118
+ if (record["ok"] === false && typeof record["reason"] === "string") return {
119
+ ok: false,
120
+ reason: record["reason"]
121
+ };
122
+ throw new TypeError("the save result must be ok with a snapshot or refused with a reason");
123
+ }
124
+ /** Parse the live catalogue the page offers. */
125
+ function parseCatalogue(value) {
126
+ const record = parseRecord(value, "the catalogue");
127
+ const providers = record["providers"];
128
+ if (!Array.isArray(providers)) throw new TypeError("the catalogue must carry providers");
129
+ if (typeof record["truncated"] !== "boolean") throw new TypeError("the catalogue truncated flag must be a boolean");
130
+ return {
131
+ providers: providers.map((provider, index) => {
132
+ const entry = parseRecord(provider, `providers[${index}]`);
133
+ if (typeof entry["provider"] !== "string" || typeof entry["name"] !== "string") throw new TypeError(`providers[${index}] needs provider and name strings`);
134
+ const models = entry["models"];
135
+ if (!Array.isArray(models)) throw new TypeError(`providers[${index}].models must be an array`);
136
+ return {
137
+ provider: entry["provider"],
138
+ name: entry["name"],
139
+ ...typeof entry["error"] === "string" ? { error: entry["error"] } : {},
140
+ models: models.map((model, modelIndex) => {
141
+ const item = parseRecord(model, `providers[${index}].models[${modelIndex}]`);
142
+ if (typeof item["route"] !== "string" || typeof item["id"] !== "string" || typeof item["name"] !== "string") throw new TypeError(`providers[${index}].models[${modelIndex}] needs route, id, and name strings`);
143
+ const efforts = item["efforts"];
144
+ if (!Array.isArray(efforts) || efforts.some((effort) => typeof effort !== "string")) throw new TypeError(`providers[${index}].models[${modelIndex}].efforts must be strings`);
145
+ return {
146
+ route: item["route"],
147
+ id: item["id"],
148
+ name: item["name"],
149
+ ...typeof item["description"] === "string" ? { description: item["description"] } : {},
150
+ ...typeof item["contextWindow"] === "number" ? { contextWindow: item["contextWindow"] } : {},
151
+ ...typeof item["maxTokens"] === "number" ? { maxTokens: item["maxTokens"] } : {},
152
+ efforts,
153
+ ...typeof item["defaultEffort"] === "string" ? { defaultEffort: item["defaultEffort"] } : {},
154
+ ...typeof item["error"] === "string" ? { error: item["error"] } : {}
155
+ };
156
+ })
157
+ };
158
+ }),
159
+ truncated: record["truncated"]
160
+ };
161
+ }
162
+ function directMethod(options) {
163
+ const descriptor = {
164
+ id: `${REMOTE_PACKAGE}#${ROLES_SERVICE}/${options.method}`,
165
+ service: ROLES_SERVICE,
166
+ namespace: ROLES_SERVICE,
167
+ method: options.method,
168
+ invocation: { kind: "direct" },
169
+ parameters: options.parameters,
170
+ result: options.result,
171
+ ...options.cancellation === true ? { cancellation: { parameter: "signal" } } : {}
172
+ };
173
+ const input = { create() {
174
+ return { parse(value) {
175
+ const record = parseRecord(value, "the method input");
176
+ const parsed = {};
177
+ for (const item of options.parameters) if (record[item.wire] !== void 0) {
178
+ const codec = item.codec;
179
+ parsed[item.wire] = codec.create().parse(record[item.wire]);
180
+ }
181
+ return parsed;
182
+ } };
183
+ } };
184
+ return {
185
+ ...descriptor,
186
+ input: () => input,
187
+ invoke(remote, named, signal) {
188
+ const namespace = Reflect.get(remote, ROLES_SERVICE);
189
+ const values = options.parameters.map((item) => named[item.wire]);
190
+ if (options.cancellation === true) values.push(signal);
191
+ return namespace[options.method](...values);
192
+ }
193
+ };
194
+ }
195
+ const statusMethod = directMethod({
196
+ method: "status",
197
+ parameters: [],
198
+ result: jsonCodec("PstackRoleFileSnapshot", parseRoleFileSnapshot, parseRoleFileSnapshot),
199
+ invoke: (remote) => remote.pstackRoles.status()
200
+ });
201
+ const catalogueMethod = directMethod({
202
+ method: "catalogue",
203
+ parameters: [],
204
+ result: jsonCodec("PstackRoleCatalogue", parseCatalogue, parseCatalogue),
205
+ cancellation: true,
206
+ invoke: (remote, _input, signal) => remote.pstackRoles.catalogue(signal)
207
+ });
208
+ const saveMethod = directMethod({
209
+ method: "save",
210
+ parameters: [parameter("request", "PstackSaveRoleRequest", (value) => {
211
+ parseSaveRoleRequest(value);
212
+ return value;
213
+ })],
214
+ result: jsonCodec("PstackSaveRoleResult", parseSaveRoleResult, parseSaveRoleResult),
215
+ cancellation: true,
216
+ invoke: (remote, request, signal) => remote.pstackRoles.save(request, signal)
217
+ });
218
+ /** Mountable contribution the Client passes to `ctx.remote.$mount()`. */
219
+ const pstackRolesRemote = {
220
+ package: REMOTE_PACKAGE,
221
+ descriptors: [
222
+ statusMethod,
223
+ catalogueMethod,
224
+ saveMethod
225
+ ],
226
+ methods: {
227
+ [`${ROLES_SERVICE}/status`]: statusMethod,
228
+ [`${ROLES_SERVICE}/catalogue`]: catalogueMethod,
229
+ [`${ROLES_SERVICE}/save`]: saveMethod
230
+ }
231
+ };
232
+ /** The same descriptors registered by the Host as its strict definitions. */
233
+ const pstackRolesContribution = pstackRolesRemote;
234
+ export { REMOTE_PACKAGE, ROLES_SERVICE, parseCatalogue, parseRoleFileSnapshot, parseSaveRoleRequest, parseSaveRoleResult, pstackRolesContribution, pstackRolesRemote };
@@ -0,0 +1,415 @@
1
+ import { join, resolve } from "node:path";
2
+ import { readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ // src/internal/role-manifest.ts
5
+ /**
6
+ * File name the role configuration lives under inside `$DSH_HOME`.
7
+ *
8
+ * It keeps the upstream name `pstack-models.mdc`, so a reader moving from the Cursor docs
9
+ * recognizes it, but stores it directly under `$DSH_HOME`; DSH does not load `.mdc` files
10
+ * on its own, and `pstack_subagent` is the only reader.
11
+ */
12
+ const PSTACK_MODELS_FILENAME = "pstack-models.mdc";
13
+ /** Values that resolve to "send no `agentOptions` and inherit the parent route". */
14
+ const INHERIT_PARENT_ALIASES = ["inherit-parent", "auto"];
15
+ /** The 17 roles, in manifest order. */
16
+ const ROLE_MANIFEST = [
17
+ {
18
+ key: "feature, refactoring",
19
+ shape: "single",
20
+ autoPanel: false
21
+ },
22
+ {
23
+ key: "bug-fix",
24
+ shape: "single",
25
+ autoPanel: false
26
+ },
27
+ {
28
+ key: "perf-issue",
29
+ shape: "single",
30
+ autoPanel: false
31
+ },
32
+ {
33
+ key: "hillclimb",
34
+ shape: "single",
35
+ autoPanel: false
36
+ },
37
+ {
38
+ key: "judgment and prose",
39
+ shape: "single",
40
+ autoPanel: false
41
+ },
42
+ {
43
+ key: "hardest tasks",
44
+ shape: "single",
45
+ autoPanel: false
46
+ },
47
+ {
48
+ key: "how explorer",
49
+ shape: "single",
50
+ autoPanel: false
51
+ },
52
+ {
53
+ key: "how explainer",
54
+ shape: "single",
55
+ autoPanel: false
56
+ },
57
+ {
58
+ key: "why investigators",
59
+ shape: "single",
60
+ autoPanel: false
61
+ },
62
+ {
63
+ key: "why synthesizer",
64
+ shape: "single",
65
+ autoPanel: false
66
+ },
67
+ {
68
+ key: "reflect tooling",
69
+ shape: "single",
70
+ autoPanel: false
71
+ },
72
+ {
73
+ key: "reflect judgment, divergent, synthesizer",
74
+ shape: "single",
75
+ autoPanel: false
76
+ },
77
+ {
78
+ key: "arena runners",
79
+ shape: "list",
80
+ autoPanel: true
81
+ },
82
+ {
83
+ key: "arena cross-judge pool",
84
+ shape: "list",
85
+ autoPanel: false
86
+ },
87
+ {
88
+ key: "swarm workers",
89
+ shape: "list",
90
+ autoPanel: false
91
+ },
92
+ {
93
+ key: "architect runners",
94
+ shape: "list",
95
+ autoPanel: true
96
+ },
97
+ {
98
+ key: "interrogate reviewers",
99
+ shape: "list",
100
+ autoPanel: true
101
+ }
102
+ ];
103
+ /** Exact manifest lookup. Callers normalize the key first. */
104
+ function findRoleSpec(key) {
105
+ return ROLE_MANIFEST.find((role) => role.key === key);
106
+ }
107
+ // src/internal/role-config.ts
108
+ /**
109
+ * The pstack role configuration: `$DSH_HOME/pstack-models.mdc`.
110
+ *
111
+ * `pstack_subagent` reads this file again on every delegation, so a change reaches the
112
+ * next child and never a child that already started.
113
+ *
114
+ * Three states stay apart because each needs a different fix: the file is missing (no
115
+ * policy, so every role inherits the parent route), unreadable (a broken install, never
116
+ * treated as "no configuration"), or loaded with per-line issues reported by line.
117
+ */
118
+ /** Absolute path of the role file for one DSH home. */
119
+ function modelsFilePath(dshHome) {
120
+ return join(dshHome, PSTACK_MODELS_FILENAME);
121
+ }
122
+ /** Resolve `$DSH_HOME` the way the Host does: the environment when set, else `~/.dsh`. */
123
+ function resolveDshHome(env = process.env, home = homedir()) {
124
+ const configured = env["DSH_HOME"];
125
+ const path = configured !== void 0 && configured.trim() !== "" ? configured : join(home, ".dsh");
126
+ const expanded = path === "~" ? home : path.startsWith("~/") || path.startsWith("~\\") ? join(home, path.slice(2)) : path;
127
+ return resolve(expanded);
128
+ }
129
+ /** Read the role file. Never throws for a missing or unreadable file. */
130
+ function readRoleConfiguration(path) {
131
+ let text;
132
+ try {
133
+ text = readFileSync(path, "utf8");
134
+ } catch (error) {
135
+ if (isMissingFile(error)) return {
136
+ status: "missing",
137
+ path
138
+ };
139
+ return {
140
+ status: "unreadable",
141
+ path,
142
+ reason: describeError(error)
143
+ };
144
+ }
145
+ return parseRoleConfiguration(text, path);
146
+ }
147
+ /** Parse role-file text. Pure: no file system access. */
148
+ function parseRoleConfiguration(text, path) {
149
+ const issues = [];
150
+ const roles = /* @__PURE__ */ new Map();
151
+ let budget;
152
+ stripFrontmatter(text).split("\n").forEach((rawLine, index) => {
153
+ const line = index + 1;
154
+ const trimmed = rawLine.trim();
155
+ if (trimmed === "") return;
156
+ if (trimmed.startsWith("#")) {
157
+ const match = /^#\s*budget:\s*(.+)$/i.exec(trimmed);
158
+ if (match !== null) budget = match[1].trim();
159
+ return;
160
+ }
161
+ const separator = trimmed.indexOf(":");
162
+ if (separator === -1) {
163
+ issues.push({
164
+ line,
165
+ message: `expected \`<role>: <value>\`, found ${JSON.stringify(trimmed)}`
166
+ });
167
+ return;
168
+ }
169
+ const rawKey = trimmed.slice(0, separator).trim();
170
+ const rawValue = trimmed.slice(separator + 1).trim();
171
+ const key = normalizeRoleName(rawKey);
172
+ if (key === "") {
173
+ issues.push({
174
+ line,
175
+ message: "the role key is empty"
176
+ });
177
+ return;
178
+ }
179
+ const spec = findRoleSpec(key);
180
+ if (spec === void 0) {
181
+ issues.push({
182
+ line,
183
+ message: unknownRoleMessage(rawKey, key)
184
+ });
185
+ return;
186
+ }
187
+ const parsed = parseRouteValue(rawValue);
188
+ const entryIssues = [...parsed.issues];
189
+ if (spec.shape === "single" && parsed.routes.length > 1) entryIssues.push("a single-route role cannot hold a comma-separated list");
190
+ for (const issue of entryIssues) issues.push({
191
+ line,
192
+ message: `${rawKey}: ${issue}`
193
+ });
194
+ if (parsed.routes.length === 0) return;
195
+ const previous = roles.get(spec.key);
196
+ if (previous !== void 0) issues.push({
197
+ line,
198
+ message: `role "${spec.key}" is already configured on line ${previous.line}; line ${line} wins`
199
+ });
200
+ roles.set(spec.key, {
201
+ line,
202
+ rawKey,
203
+ key: spec.key,
204
+ spec,
205
+ routes: parsed.routes,
206
+ issues: entryIssues
207
+ });
208
+ });
209
+ return budget === void 0 ? {
210
+ status: "loaded",
211
+ path,
212
+ roles,
213
+ issues
214
+ } : {
215
+ status: "loaded",
216
+ path,
217
+ budget,
218
+ roles,
219
+ issues
220
+ };
221
+ }
222
+ /** Parse a role value: one route, or a comma-separated list of routes. */
223
+ function parseRouteValue(raw) {
224
+ const routes = [];
225
+ const issues = [];
226
+ const segments = raw.split(",");
227
+ segments.forEach((segment, index) => {
228
+ const trimmed = segment.trim();
229
+ if (trimmed === "") {
230
+ if (index !== segments.length - 1) issues.push(`entry ${index + 1} of the list is empty`);
231
+ return;
232
+ }
233
+ const parsed = parseRoute(trimmed);
234
+ if (parsed.route === void 0) {
235
+ issues.push(parsed.issue);
236
+ return;
237
+ }
238
+ routes.push(parsed.route);
239
+ });
240
+ if (routes.length === 0 && issues.length === 0) issues.push("the value is empty; use `inherit-parent`, `auto`, or `<provider>/<model> [effort]`");
241
+ return {
242
+ routes,
243
+ issues
244
+ };
245
+ }
246
+ /** Parse one entry of a role value. */
247
+ function parseRoute(raw) {
248
+ const value = raw.trim();
249
+ if (value === "") return { issue: "the value is empty" };
250
+ const alias = value.toLowerCase();
251
+ if (INHERIT_PARENT_ALIASES.includes(alias)) return { route: {
252
+ kind: "inherit",
253
+ alias
254
+ } };
255
+ const match = /^([^\s/,[]+)\/([^\s/,[\]]+(?:\/[^\s/,[\]]+)*)(?:\s+\[([^[\]]*)\])?$/.exec(value);
256
+ if (match === null) return { issue: `${JSON.stringify(value)} is not a route; use \`inherit-parent\`, \`auto\`, or \`<provider>/<model> [effort]\`` };
257
+ const provider = match[1];
258
+ const model = match[2];
259
+ const effort = match[3];
260
+ if (effort === void 0) return { route: {
261
+ kind: "route",
262
+ provider,
263
+ model
264
+ } };
265
+ const trimmedEffort = effort.trim();
266
+ if (trimmedEffort === "") return { issue: `${JSON.stringify(value)} has empty brackets; write \`<provider>/<model> [effort]\` or drop the brackets` };
267
+ if (/\s/.test(trimmedEffort)) return { issue: `${JSON.stringify(value)} has a multi-word effort; an effort is one adapter-owned token` };
268
+ return { route: {
269
+ kind: "route",
270
+ provider,
271
+ model,
272
+ effort: trimmedEffort
273
+ } };
274
+ }
275
+ /** Raised when a role's configured list cannot be selected from. */
276
+ var RouteSelectionError = class extends Error {
277
+ constructor(message) {
278
+ super(message);
279
+ this.name = "RouteSelectionError";
280
+ }
281
+ };
282
+ /**
283
+ * Expand one role into the routes this call starts.
284
+ *
285
+ * Automatic panels start one child per entry. Another list role selects one entry, the
286
+ * first by default; `roleIndex` chooses which. A single role holds exactly one route and
287
+ * rejects `roleIndex`.
288
+ */
289
+ function selectRoleRoutes(spec, entry, roleIndex) {
290
+ if (spec.shape === "single" && roleIndex !== void 0) throw new RouteSelectionError(`role "${spec.key}" holds a single route, so \`roleIndex\` does not apply; drop \`roleIndex\` or use a list-valued role (${listRoleKeys().join(", ")})`);
291
+ if (spec.autoPanel && roleIndex !== void 0) throw new RouteSelectionError(`role "${spec.key}" is an automatic panel: it starts one child per entry, so \`roleIndex\` does not apply`);
292
+ if (entry === void 0) return {
293
+ role: spec,
294
+ mode: spec.autoPanel ? "panel" : "single",
295
+ configured: false,
296
+ routes: [{ kind: "inherit" }],
297
+ total: 1
298
+ };
299
+ if (entry.issues.length > 0) throw new RouteSelectionError(`role "${spec.key}" has invalid configuration on line ${entry.line}: ${entry.issues.join("; ")}. Fix the role file before delegating.`);
300
+ if (spec.autoPanel) return {
301
+ role: spec,
302
+ mode: "panel",
303
+ configured: true,
304
+ routes: entry.routes,
305
+ total: entry.routes.length
306
+ };
307
+ if (spec.shape === "list") {
308
+ const index = roleIndex ?? 0;
309
+ if (index >= entry.routes.length) throw new RouteSelectionError(`role "${spec.key}" has ${entry.routes.length} entr${entry.routes.length === 1 ? "y" : "ies"}, so \`roleIndex\` ${index} is out of range; valid indexes are ${entry.routes.map((_route, position) => position).join(", ")}`);
310
+ return {
311
+ role: spec,
312
+ mode: "selected",
313
+ configured: true,
314
+ routes: [entry.routes[index]],
315
+ total: entry.routes.length,
316
+ roleIndex: index
317
+ };
318
+ }
319
+ return {
320
+ role: spec,
321
+ mode: "single",
322
+ configured: true,
323
+ routes: [entry.routes[0]],
324
+ total: entry.routes.length
325
+ };
326
+ }
327
+ /** Keys of the list-valued roles, including the automatic panels. */
328
+ function listRoleKeys() {
329
+ return ROLE_MANIFEST.filter((role) => role.shape === "list").map((role) => role.key);
330
+ }
331
+ /** Raised when a role name does not match the manifest. */
332
+ var RoleNameError = class extends Error {
333
+ constructor(message) {
334
+ super(message);
335
+ this.name = "RoleNameError";
336
+ }
337
+ };
338
+ /** Normalize a role name for lookup. Case, spacing, spacing around a comma, and a trailing colon are formatting. */
339
+ function normalizeRoleName(raw) {
340
+ return raw.normalize("NFC").trim().toLowerCase().replace(/\s+/g, " ").replace(/\s*,\s*/g, ", ").replace(/:+$/, "").trim();
341
+ }
342
+ /** Resolve a role name, or throw a diagnostic that names the near misses. */
343
+ function requireRole(raw) {
344
+ const spec = findRoleSpec(normalizeRoleName(raw));
345
+ if (spec !== void 0) return spec;
346
+ const suggestions = suggestRoles(raw);
347
+ const hint = suggestions.length === 0 ? "No role key is close to it." : `Closest role keys: ${suggestions.map((key) => `"${key}"`).join(", ")}.`;
348
+ throw new RoleNameError(`unknown pstack role ${JSON.stringify(raw)}. ${hint} Role names come from the role manifest, not from free text: pass the exact key, or omit \`role\` to inherit the parent route.`);
349
+ }
350
+ function suggestRoles(raw, limit = 3) {
351
+ const target = normalizeRoleName(raw);
352
+ if (target === "") return [];
353
+ return ROLE_MANIFEST.map((role) => {
354
+ const shared = sharedTokenCount(target, role.key);
355
+ const distance = editDistance(target, role.key);
356
+ const contains = role.key.includes(target) || target.includes(role.key);
357
+ const score = (contains ? 1e3 : 0) + shared * 100 - distance + Math.min(role.key.length, target.length);
358
+ return {
359
+ key: role.key,
360
+ score,
361
+ distance,
362
+ shared,
363
+ contains
364
+ };
365
+ }).filter((candidate) => candidate.contains || candidate.shared > 0 || candidate.distance <= 4).sort((left, right) => right.score - left.score).slice(0, limit).map((candidate) => candidate.key);
366
+ }
367
+ function unknownRoleMessage(rawKey, key) {
368
+ const suggestions = suggestRoles(key);
369
+ const hint = suggestions.length === 0 ? "no role key is close to it" : `closest role keys: ${suggestions.map((candidate) => `"${candidate}"`).join(", ")}`;
370
+ return `unknown role ${JSON.stringify(rawKey)} (${hint}). A line for a retired or misspelled role is reported rather than ignored.`;
371
+ }
372
+ function sharedTokenCount(left, right) {
373
+ const rightTokens = new Set(tokenize(right));
374
+ return [...new Set(tokenize(left))].filter((token) => rightTokens.has(token)).length;
375
+ }
376
+ function tokenize(value) {
377
+ return value.split(/[^a-z0-9]+/).filter((token) => token !== "");
378
+ }
379
+ /** Levenshtein distance, iterative with one row of state. */
380
+ function editDistance(left, right) {
381
+ if (left === right) return 0;
382
+ if (left === "") return right.length;
383
+ if (right === "") return left.length;
384
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
385
+ for (let row = 1; row <= left.length; row += 1) {
386
+ const current = [row];
387
+ for (let column = 1; column <= right.length; column += 1) {
388
+ const substitution = previous[column - 1] + (left[row - 1] === right[column - 1] ? 0 : 1);
389
+ const deletion = previous[column] + 1;
390
+ const insertion = current[column - 1] + 1;
391
+ current.push(Math.min(substitution, deletion, insertion));
392
+ }
393
+ previous = current;
394
+ }
395
+ return previous[right.length];
396
+ }
397
+ function stripFrontmatter(text) {
398
+ const normalized = text.replaceAll("\r\n", "\n");
399
+ if (!normalized.startsWith("---\n")) return normalized;
400
+ const end = normalized.indexOf("\n---", 3);
401
+ if (end === -1) return normalized;
402
+ const after = normalized.indexOf("\n", end + 1);
403
+ return after === -1 ? "" : normalized.slice(after + 1);
404
+ }
405
+ function isMissingFile(error) {
406
+ return typeof error === "object" && error !== null && error.code === "ENOENT";
407
+ }
408
+ function describeError(error) {
409
+ if (error instanceof Error) {
410
+ const code = error.code;
411
+ return typeof code === "string" ? `${code}: ${error.message}` : error.message;
412
+ }
413
+ return String(error);
414
+ }
415
+ export { resolveDshHome as a, ROLE_MANIFEST as c, requireRole as i, findRoleSpec as l, normalizeRoleName as n, selectRoleRoutes as o, readRoleConfiguration as r, INHERIT_PARENT_ALIASES as s, modelsFilePath as t };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The one pstack role manifest.
3
+ *
4
+ * Keys are matched exactly after normalization, and commas belong to some keys
5
+ * (`feature, refactoring`, `reflect judgment, divergent, synthesizer`), so nothing here
6
+ * may split a key on a comma. A second literal role list anywhere is a bug.
7
+ *
8
+ * This module has no Node imports: the bundle imports it directly.
9
+ */
10
+ /** Whether a role holds one route or a comma-separated list of routes. */
11
+ type RoleValueShape = "single" | "list";
12
+ interface RoleSpec {
13
+ /** Exact key as written in the role file. Commas belong to the key. */
14
+ readonly key: string;
15
+ readonly shape: RoleValueShape;
16
+ /** A list role that starts one child per entry automatically. */
17
+ readonly autoPanel: boolean;
18
+ }
19
+ export { RoleValueShape as n, RoleSpec as t };