@azure-id/orc 0.55.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 (449) hide show
  1. package/CHANGELOG.md +2017 -0
  2. package/README-id.md +521 -0
  3. package/README.md +485 -0
  4. package/bin/build-agents.js +86 -0
  5. package/bin/cli.js +29050 -0
  6. package/bin/mockrun-catalog.js +267 -0
  7. package/bin/onboarding-content.js +172 -0
  8. package/bin/pricing.json +200 -0
  9. package/bin/providers.json +437 -0
  10. package/bin/ui.js +95 -0
  11. package/bin/verify-contracts.js +3277 -0
  12. package/bin/verify-package.js +498 -0
  13. package/bin/webui/api.js +1163 -0
  14. package/bin/webui/app.html +198 -0
  15. package/bin/webui/css/00-tokens.css +92 -0
  16. package/bin/webui/css/01-base.css +42 -0
  17. package/bin/webui/css/02-shell.css +103 -0
  18. package/bin/webui/css/03-components.css +462 -0
  19. package/bin/webui/css/04-motion.css +91 -0
  20. package/bin/webui/css/05-tour.css +100 -0
  21. package/bin/webui/css/06-responsive.css +108 -0
  22. package/bin/webui/css/panels/boundary.css +26 -0
  23. package/bin/webui/css/panels/challenge.css +151 -0
  24. package/bin/webui/css/panels/crosslink.css +194 -0
  25. package/bin/webui/css/panels/docs.css +254 -0
  26. package/bin/webui/css/panels/experiment.css +23 -0
  27. package/bin/webui/css/panels/extra.css +536 -0
  28. package/bin/webui/css/panels/flow.css +177 -0
  29. package/bin/webui/css/panels/handoff.css +67 -0
  30. package/bin/webui/css/panels/knowledge.css +86 -0
  31. package/bin/webui/css/panels/learn.css +154 -0
  32. package/bin/webui/css/panels/maintenance.css +55 -0
  33. package/bin/webui/css/panels/mockrun.css +268 -0
  34. package/bin/webui/css/panels/overview.css +100 -0
  35. package/bin/webui/css/panels/pact.css +43 -0
  36. package/bin/webui/css/panels/runs.css +105 -0
  37. package/bin/webui/css/panels/settings.css +227 -0
  38. package/bin/webui/css/panels/stats.css +87 -0
  39. package/bin/webui/fixtures/boundary.js +92 -0
  40. package/bin/webui/fixtures/challenge.js +825 -0
  41. package/bin/webui/fixtures/crosslink.js +31 -0
  42. package/bin/webui/fixtures/docs.js +982 -0
  43. package/bin/webui/fixtures/extra.js +1934 -0
  44. package/bin/webui/fixtures/flow.js +81 -0
  45. package/bin/webui/fixtures/handoff.js +35 -0
  46. package/bin/webui/fixtures/index.js +486 -0
  47. package/bin/webui/fixtures/knowledge.js +291 -0
  48. package/bin/webui/fixtures/maintenance.js +42 -0
  49. package/bin/webui/fixtures/mockrun.js +36 -0
  50. package/bin/webui/fixtures/pact.js +111 -0
  51. package/bin/webui/fixtures/runs.js +109 -0
  52. package/bin/webui/fixtures/settings.js +78 -0
  53. package/bin/webui/fixtures/shell.js +48 -0
  54. package/bin/webui/fixtures/stats.js +104 -0
  55. package/bin/webui/i18n/TERMS.md +134 -0
  56. package/bin/webui/i18n/en/banner.json +29 -0
  57. package/bin/webui/i18n/en/boundary.json +20 -0
  58. package/bin/webui/i18n/en/challenge.json +106 -0
  59. package/bin/webui/i18n/en/common.json +69 -0
  60. package/bin/webui/i18n/en/crosslink.json +69 -0
  61. package/bin/webui/i18n/en/docs.json +175 -0
  62. package/bin/webui/i18n/en/experiment.json +17 -0
  63. package/bin/webui/i18n/en/extra.json +313 -0
  64. package/bin/webui/i18n/en/flow.json +30 -0
  65. package/bin/webui/i18n/en/handoff.json +37 -0
  66. package/bin/webui/i18n/en/knowledge.json +141 -0
  67. package/bin/webui/i18n/en/learn.json +16 -0
  68. package/bin/webui/i18n/en/maintenance.json +52 -0
  69. package/bin/webui/i18n/en/mockrun.json +19 -0
  70. package/bin/webui/i18n/en/nav.json +19 -0
  71. package/bin/webui/i18n/en/overview.json +89 -0
  72. package/bin/webui/i18n/en/pact.json +36 -0
  73. package/bin/webui/i18n/en/runs.json +51 -0
  74. package/bin/webui/i18n/en/settings.json +49 -0
  75. package/bin/webui/i18n/en/stats.json +69 -0
  76. package/bin/webui/i18n/en/tour.json +33 -0
  77. package/bin/webui/i18n/id/banner.json +29 -0
  78. package/bin/webui/i18n/id/boundary.json +20 -0
  79. package/bin/webui/i18n/id/challenge.json +106 -0
  80. package/bin/webui/i18n/id/common.json +70 -0
  81. package/bin/webui/i18n/id/crosslink.json +69 -0
  82. package/bin/webui/i18n/id/docs.json +175 -0
  83. package/bin/webui/i18n/id/experiment.json +17 -0
  84. package/bin/webui/i18n/id/extra.json +313 -0
  85. package/bin/webui/i18n/id/flow.json +30 -0
  86. package/bin/webui/i18n/id/handoff.json +37 -0
  87. package/bin/webui/i18n/id/knowledge.json +141 -0
  88. package/bin/webui/i18n/id/learn.json +16 -0
  89. package/bin/webui/i18n/id/maintenance.json +52 -0
  90. package/bin/webui/i18n/id/mockrun.json +19 -0
  91. package/bin/webui/i18n/id/nav.json +19 -0
  92. package/bin/webui/i18n/id/overview.json +89 -0
  93. package/bin/webui/i18n/id/pact.json +36 -0
  94. package/bin/webui/i18n/id/runs.json +51 -0
  95. package/bin/webui/i18n/id/settings.json +49 -0
  96. package/bin/webui/i18n/id/stats.json +69 -0
  97. package/bin/webui/i18n/id/tour.json +33 -0
  98. package/bin/webui/js/00-core.js +115 -0
  99. package/bin/webui/js/01-i18n.js +149 -0
  100. package/bin/webui/js/02-ui.js +211 -0
  101. package/bin/webui/js/03-md.js +286 -0
  102. package/bin/webui/js/04-router.js +51 -0
  103. package/bin/webui/js/05-banners.js +172 -0
  104. package/bin/webui/js/06-edit.js +183 -0
  105. package/bin/webui/js/90-tour.js +488 -0
  106. package/bin/webui/js/91-shortcuts.js +126 -0
  107. package/bin/webui/js/99-boot.js +118 -0
  108. package/bin/webui/js/panels/boundary.js +104 -0
  109. package/bin/webui/js/panels/challenge.js +781 -0
  110. package/bin/webui/js/panels/crosslink.js +654 -0
  111. package/bin/webui/js/panels/docs.js +1338 -0
  112. package/bin/webui/js/panels/experiment.js +98 -0
  113. package/bin/webui/js/panels/extra.js +3011 -0
  114. package/bin/webui/js/panels/flow.js +297 -0
  115. package/bin/webui/js/panels/handoff.js +169 -0
  116. package/bin/webui/js/panels/knowledge.js +890 -0
  117. package/bin/webui/js/panels/learn.js +220 -0
  118. package/bin/webui/js/panels/maintenance.js +235 -0
  119. package/bin/webui/js/panels/mockrun.js +227 -0
  120. package/bin/webui/js/panels/overview.js +447 -0
  121. package/bin/webui/js/panels/pact.js +189 -0
  122. package/bin/webui/js/panels/runs.js +416 -0
  123. package/bin/webui/js/panels/settings.js +573 -0
  124. package/bin/webui/js/panels/stats.js +378 -0
  125. package/bin/webui/serve.js +557 -0
  126. package/mock-run/INDEX.md +106 -0
  127. package/mock-run/a-normal-day.md +587 -0
  128. package/mock-run/context-combiner.md +100 -0
  129. package/mock-run/extra-recovery.md +330 -0
  130. package/mock-run/extra-slots.md +177 -0
  131. package/mock-run/media/README.md +43 -0
  132. package/mock-run/orc-aftermath.md +392 -0
  133. package/mock-run/orc-boundary.md +356 -0
  134. package/mock-run/orc-brainstorm.md +177 -0
  135. package/mock-run/orc-budget.md +534 -0
  136. package/mock-run/orc-challenge-council.md +262 -0
  137. package/mock-run/orc-challenge.md +416 -0
  138. package/mock-run/orc-cli.md +200 -0
  139. package/mock-run/orc-diy.md +129 -0
  140. package/mock-run/orc-doc.md +448 -0
  141. package/mock-run/orc-explain.md +86 -0
  142. package/mock-run/orc-export.md +423 -0
  143. package/mock-run/orc-extra.md +392 -0
  144. package/mock-run/orc-fast.md +106 -0
  145. package/mock-run/orc-grill.md +151 -0
  146. package/mock-run/orc-handoff.md +480 -0
  147. package/mock-run/orc-pact.md +421 -0
  148. package/mock-run/orc-pattern.md +112 -0
  149. package/mock-run/orc-plan.md +108 -0
  150. package/mock-run/orc-pr-setup.md +126 -0
  151. package/mock-run/orc-quick.md +113 -0
  152. package/mock-run/orc-route.md +93 -0
  153. package/mock-run/orc-ui.md +125 -0
  154. package/mock-run/orc-ultra.md +103 -0
  155. package/mock-run/orc.md +157 -0
  156. package/mock-run/the-example-project.md +55 -0
  157. package/package.json +39 -0
  158. package/templates/agents/MODEL-MAPPING.md +168 -0
  159. package/templates/agents/orc-advisor-fable-5.md +50 -0
  160. package/templates/agents/orc-advisor-opus-5-xhigh.md +56 -0
  161. package/templates/agents/orc-analyst-fable-5.md +115 -0
  162. package/templates/agents/orc-analyze-mini-opus-5-med.md +60 -0
  163. package/templates/agents/orc-analyze-mini-sonnet-5-high.md +58 -0
  164. package/templates/agents/orc-challenge-advisor-opus-5-med.md +75 -0
  165. package/templates/agents/orc-challenge-contrarian-opus-5-high.md +110 -0
  166. package/templates/agents/orc-challenge-executor-opus-5-med.md +114 -0
  167. package/templates/agents/orc-challenge-expansionist-opus-5-med.md +112 -0
  168. package/templates/agents/orc-challenge-judge-opus-5-high.md +132 -0
  169. package/templates/agents/orc-challenge-outsider-opus-5-low.md +109 -0
  170. package/templates/agents/orc-challenge-principles-opus-5-high.md +109 -0
  171. package/templates/agents/orc-challenge-reader-opus-5-low.md +90 -0
  172. package/templates/agents/orc-claude-writer-opus-4-8-high.md +53 -0
  173. package/templates/agents/orc-claude-writer-opus-5-med.md +55 -0
  174. package/templates/agents/orc-context-combiner-opus-5-high.md +88 -0
  175. package/templates/agents/orc-doc-checker-opus-5-low.md +108 -0
  176. package/templates/agents/orc-doc-writer-opus-5-med.md +134 -0
  177. package/templates/agents/orc-executor-haiku-4-5.md +113 -0
  178. package/templates/agents/orc-executor-opus-4-7-high.md +114 -0
  179. package/templates/agents/orc-executor-opus-4-7-med.md +114 -0
  180. package/templates/agents/orc-executor-opus-4-8-high.md +114 -0
  181. package/templates/agents/orc-executor-opus-5-high.md +114 -0
  182. package/templates/agents/orc-executor-opus-5-low.md +114 -0
  183. package/templates/agents/orc-executor-opus-5-med.md +114 -0
  184. package/templates/agents/orc-executor-sonnet-4-6-high.md +114 -0
  185. package/templates/agents/orc-executor-sonnet-4-6-med.md +114 -0
  186. package/templates/agents/orc-executor-sonnet-5-high.md +114 -0
  187. package/templates/agents/orc-judge-fable-5.md +79 -0
  188. package/templates/agents/orc-judge-opus-5-xhigh.md +85 -0
  189. package/templates/agents/orc-learn-writer-opus-5-low.md +73 -0
  190. package/templates/agents/orc-pattern-codifier-opus-5-med.md +65 -0
  191. package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +63 -0
  192. package/templates/agents/orc-planner-fable-5.md +152 -0
  193. package/templates/agents/orc-planner-mini-opus-5-med.md +69 -0
  194. package/templates/agents/orc-planner-mini-sonnet-5-high.md +67 -0
  195. package/templates/agents/orc-planner-opus-5-med.md +160 -0
  196. package/templates/agents/orc-retro-opus-5-med.md +73 -0
  197. package/templates/agents/orc-retro-sonnet-5-high.md +72 -0
  198. package/templates/agents/orc-reviewer-fable-5.md +57 -0
  199. package/templates/agents/orc-reviewer-opus-5-med.md +60 -0
  200. package/templates/agents/orc-scout-opus-5-low.md +40 -0
  201. package/templates/agents/orc-scout-sonnet-4-6-high.md +39 -0
  202. package/templates/agents/orc-system-analyst-opus-5-high.md +120 -0
  203. package/templates/agents/orc-test-author-opus-5-med.md +71 -0
  204. package/templates/agents/orc-trace-writer-haiku-4-5.md +107 -0
  205. package/templates/agents/orc-verifier-opus-5-med.md +69 -0
  206. package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +79 -0
  207. package/templates/agents/orc-wiki-scanner-opus-5-med.md +81 -0
  208. package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +106 -0
  209. package/templates/commands/orc-aftermath.md +49 -0
  210. package/templates/commands/orc-analyze.md +21 -0
  211. package/templates/commands/orc-boundary.md +46 -0
  212. package/templates/commands/orc-brainstorm.md +51 -0
  213. package/templates/commands/orc-budget.md +54 -0
  214. package/templates/commands/orc-challenge.md +58 -0
  215. package/templates/commands/orc-claude.md +14 -0
  216. package/templates/commands/orc-diy.md +13 -0
  217. package/templates/commands/orc-doc.md +128 -0
  218. package/templates/commands/orc-explain.md +20 -0
  219. package/templates/commands/orc-export.md +46 -0
  220. package/templates/commands/orc-fast.md +15 -0
  221. package/templates/commands/orc-grill.md +40 -0
  222. package/templates/commands/orc-handoff.md +52 -0
  223. package/templates/commands/orc-learn.md +17 -0
  224. package/templates/commands/orc-mini.md +12 -0
  225. package/templates/commands/orc-pact.md +44 -0
  226. package/templates/commands/orc-pattern.md +15 -0
  227. package/templates/commands/orc-plan.md +22 -0
  228. package/templates/commands/orc-poly.md +21 -0
  229. package/templates/commands/orc-pr-driver.md +30 -0
  230. package/templates/commands/orc-pr-setup.md +31 -0
  231. package/templates/commands/orc-quick.md +33 -0
  232. package/templates/commands/orc-retro.md +17 -0
  233. package/templates/commands/orc-route.md +41 -0
  234. package/templates/commands/orc-ultra.md +17 -0
  235. package/templates/commands/orc-verify.md +11 -0
  236. package/templates/commands/orc-wiki.md +42 -0
  237. package/templates/commands/orc.md +16 -0
  238. package/templates/hooks/orc-effort-guard.js +178 -0
  239. package/templates/hooks/orc-statusline.js +305 -0
  240. package/templates/hooks/orc-trace.js +471 -0
  241. package/templates/hooks/orc-update-lib.js +125 -0
  242. package/templates/skills/_shared/README.md +50 -0
  243. package/templates/skills/_shared/detecting-artifacts.md +90 -0
  244. package/templates/skills/_shared/drift-recovery.md +98 -0
  245. package/templates/skills/_shared/extra-dispatch.md +1051 -0
  246. package/templates/skills/_shared/fable5-override.md +56 -0
  247. package/templates/skills/_shared/fallback-handoff.md +42 -0
  248. package/templates/skills/_shared/gh-stack-commands.md +65 -0
  249. package/templates/skills/_shared/gotchas.md +177 -0
  250. package/templates/skills/_shared/interview.md +144 -0
  251. package/templates/skills/_shared/lane-suspend.md +82 -0
  252. package/templates/skills/_shared/opus5-only.md +131 -0
  253. package/templates/skills/_shared/pr-templates.md +106 -0
  254. package/templates/skills/_shared/read-ladder.md +54 -0
  255. package/templates/skills/_shared/return-validation.md +145 -0
  256. package/templates/skills/_shared/smoke-gate.md +28 -0
  257. package/templates/skills/_shared/stack-plan.md +135 -0
  258. package/templates/skills/_shared/untrusted-input.md +63 -0
  259. package/templates/skills/context-combiner/SKILL.md +187 -0
  260. package/templates/skills/context-combiner/schemas/combined-report.md +78 -0
  261. package/templates/skills/context-combiner/schemas/combined-requirement-spec.md +87 -0
  262. package/templates/skills/orc/README.md +150 -0
  263. package/templates/skills/orc/SKILL.md +526 -0
  264. package/templates/skills/orc/config.md +389 -0
  265. package/templates/skills/orc/examples/full-run-mock.md +73 -0
  266. package/templates/skills/orc/references/analyst-gates.md +124 -0
  267. package/templates/skills/orc/references/effort-and-mode.md +224 -0
  268. package/templates/skills/orc/references/house-rules.md +20 -0
  269. package/templates/skills/orc/references/intake.md +120 -0
  270. package/templates/skills/orc/references/log-protocol.md +39 -0
  271. package/templates/skills/orc/references/pattern-gate.md +89 -0
  272. package/templates/skills/orc/references/plan-handoff.md +84 -0
  273. package/templates/skills/orc/references/preflight-report.md +220 -0
  274. package/templates/skills/orc/references/security-checklist.md +39 -0
  275. package/templates/skills/orc/references/stop-and-resume.md +160 -0
  276. package/templates/skills/orc/references/trace-protocol.md +314 -0
  277. package/templates/skills/orc/references/ultra-mode.md +123 -0
  278. package/templates/skills/orc/references/wave-grouping.md +121 -0
  279. package/templates/skills/orc/references/wiki-consult.md +177 -0
  280. package/templates/skills/orc/schemas/checkpoint.md +122 -0
  281. package/templates/skills/orc/schemas/intent-spec.md +42 -0
  282. package/templates/skills/orc/schemas/planning-output.md +279 -0
  283. package/templates/skills/orc/subskills/orc-checkpoint/SKILL.md +55 -0
  284. package/templates/skills/orc/subskills/orc-execution/SKILL.md +72 -0
  285. package/templates/skills/orc/subskills/orc-execution/core.md +151 -0
  286. package/templates/skills/orc/subskills/orc-execution/subagent.md +13 -0
  287. package/templates/skills/orc/subskills/orc-planner/SKILL.md +264 -0
  288. package/templates/skills/orc/subskills/orc-planner-mini/SKILL.md +121 -0
  289. package/templates/skills/orc/subskills/orc-pr/SKILL.md +51 -0
  290. package/templates/skills/orc/subskills/orc-pr/pr.md +22 -0
  291. package/templates/skills/orc/subskills/orc-pr/stack-gate.md +108 -0
  292. package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +76 -0
  293. package/templates/skills/orc/subskills/orc-review-verify/core.md +135 -0
  294. package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -0
  295. package/templates/skills/orc/subskills/orc-testgen/SKILL.md +45 -0
  296. package/templates/skills/orc/subskills/orc-testgen/core.md +59 -0
  297. package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -0
  298. package/templates/skills/orc-advisor/SKILL.md +60 -0
  299. package/templates/skills/orc-aftermath/SKILL.md +136 -0
  300. package/templates/skills/orc-aftermath/references/report.md +95 -0
  301. package/templates/skills/orc-analyze/SKILL.md +220 -0
  302. package/templates/skills/orc-analyze/examples/analyze-mock.md +42 -0
  303. package/templates/skills/orc-analyze/references/branching.md +51 -0
  304. package/templates/skills/orc-analyze/references/deep-mode.md +27 -0
  305. package/templates/skills/orc-analyze/references/thin-input.md +77 -0
  306. package/templates/skills/orc-analyze/schemas/report-audit.md +83 -0
  307. package/templates/skills/orc-analyze/schemas/report-prose.md +63 -0
  308. package/templates/skills/orc-analyze/schemas/report-requirement.md +78 -0
  309. package/templates/skills/orc-analyze/schemas/requirement-spec.md +77 -0
  310. package/templates/skills/orc-analyze-mini/SKILL.md +112 -0
  311. package/templates/skills/orc-analyze-mini/examples/quick-analysis-mock.md +31 -0
  312. package/templates/skills/orc-boundary/SKILL.md +208 -0
  313. package/templates/skills/orc-boundary/references/card.md +78 -0
  314. package/templates/skills/orc-boundary/references/gate.md +113 -0
  315. package/templates/skills/orc-brainstorm/SKILL.md +339 -0
  316. package/templates/skills/orc-brainstorm/references/brainstorm-doc.md +133 -0
  317. package/templates/skills/orc-brainstorm/references/lenses.md +152 -0
  318. package/templates/skills/orc-budget/SKILL.md +225 -0
  319. package/templates/skills/orc-budget/references/corpus.md +87 -0
  320. package/templates/skills/orc-challenge/README.md +142 -0
  321. package/templates/skills/orc-challenge/SKILL.md +255 -0
  322. package/templates/skills/orc-challenge/examples/code-module.md +110 -0
  323. package/templates/skills/orc-challenge/examples/council-full-roster.md +273 -0
  324. package/templates/skills/orc-challenge/examples/tsd-two-iterations.md +213 -0
  325. package/templates/skills/orc-challenge/references/conservation.md +107 -0
  326. package/templates/skills/orc-challenge/references/council.md +315 -0
  327. package/templates/skills/orc-challenge/references/cycle-state.md +185 -0
  328. package/templates/skills/orc-challenge/references/dimensions.md +121 -0
  329. package/templates/skills/orc-challenge/references/fix-brief.md +130 -0
  330. package/templates/skills/orc-challenge/references/intake.md +171 -0
  331. package/templates/skills/orc-challenge/references/kinds.md +44 -0
  332. package/templates/skills/orc-challenge/references/plain-english.md +98 -0
  333. package/templates/skills/orc-challenge/references/rubric.md +109 -0
  334. package/templates/skills/orc-challenge/references/sealed-slice.md +97 -0
  335. package/templates/skills/orc-challenge/references/verdict-doc.md +154 -0
  336. package/templates/skills/orc-claude/SKILL.md +199 -0
  337. package/templates/skills/orc-claude/examples/claude-run-mock.md +65 -0
  338. package/templates/skills/orc-claude/references/refresh.md +76 -0
  339. package/templates/skills/orc-claude/references/template.md +109 -0
  340. package/templates/skills/orc-diy/README.md +142 -0
  341. package/templates/skills/orc-diy/SKILL.md +70 -0
  342. package/templates/skills/orc-diy/references/blocks/analyze.md +24 -0
  343. package/templates/skills/orc-diy/references/blocks/execution.md +42 -0
  344. package/templates/skills/orc-diy/references/blocks/extra.md +55 -0
  345. package/templates/skills/orc-diy/references/blocks/header.md +55 -0
  346. package/templates/skills/orc-diy/references/blocks/mock-example.md +22 -0
  347. package/templates/skills/orc-diy/references/blocks/pattern.md +18 -0
  348. package/templates/skills/orc-diy/references/blocks/planning.md +34 -0
  349. package/templates/skills/orc-diy/references/blocks/review.md +18 -0
  350. package/templates/skills/orc-diy/references/blocks/scoring.md +16 -0
  351. package/templates/skills/orc-diy/references/blocks/security.md +25 -0
  352. package/templates/skills/orc-diy/references/blocks/ship.md +25 -0
  353. package/templates/skills/orc-diy/references/blocks/summary.md +18 -0
  354. package/templates/skills/orc-diy/references/blocks/testgen.md +17 -0
  355. package/templates/skills/orc-diy/references/blocks/trace.md +28 -0
  356. package/templates/skills/orc-diy/references/blocks/verify.md +25 -0
  357. package/templates/skills/orc-diy/references/blocks/wiki.md +26 -0
  358. package/templates/skills/orc-diy/references/compile.md +59 -0
  359. package/templates/skills/orc-diy/references/flow-schema.md +100 -0
  360. package/templates/skills/orc-diy/references/locked-blocks.md +37 -0
  361. package/templates/skills/orc-doc/README.md +229 -0
  362. package/templates/skills/orc-doc/SKILL.md +444 -0
  363. package/templates/skills/orc-doc/examples/orc-doc-prd-run.md +325 -0
  364. package/templates/skills/orc-doc/references/chunking.md +527 -0
  365. package/templates/skills/orc-doc/references/gates.md +311 -0
  366. package/templates/skills/orc-doc/references/generation-rules.md +122 -0
  367. package/templates/skills/orc-doc/references/house-rules.md +170 -0
  368. package/templates/skills/orc-doc/references/import-targets.md +40 -0
  369. package/templates/skills/orc-doc/references/plain-language.md +66 -0
  370. package/templates/skills/orc-doc/references/portable-markdown.md +74 -0
  371. package/templates/skills/orc-doc/references/resume-protocol.md +228 -0
  372. package/templates/skills/orc-doc/references/templates/collaboration.md +118 -0
  373. package/templates/skills/orc-doc/references/templates/prd.md +133 -0
  374. package/templates/skills/orc-doc/references/templates/report.md +102 -0
  375. package/templates/skills/orc-doc/references/templates/tsd.md +122 -0
  376. package/templates/skills/orc-doc/references/templates/workflow.md +112 -0
  377. package/templates/skills/orc-explain/SKILL.md +57 -0
  378. package/templates/skills/orc-export/SKILL.md +154 -0
  379. package/templates/skills/orc-fast/SKILL.md +188 -0
  380. package/templates/skills/orc-grill/SKILL.md +207 -0
  381. package/templates/skills/orc-grill/references/grill-doc.md +105 -0
  382. package/templates/skills/orc-handoff/SKILL.md +204 -0
  383. package/templates/skills/orc-handoff/references/handoff-log.md +67 -0
  384. package/templates/skills/orc-handoff/references/surfaces.md +88 -0
  385. package/templates/skills/orc-judge/SKILL.md +82 -0
  386. package/templates/skills/orc-learn/SKILL.md +156 -0
  387. package/templates/skills/orc-learn/examples/learn-run-mock.md +61 -0
  388. package/templates/skills/orc-learn/references/deepen.md +70 -0
  389. package/templates/skills/orc-learn/references/refresh.md +65 -0
  390. package/templates/skills/orc-learn/references/template-knowledge.md +83 -0
  391. package/templates/skills/orc-learn/references/template-learning.md +67 -0
  392. package/templates/skills/orc-mini/SKILL.md +226 -0
  393. package/templates/skills/orc-mini/examples/mini-run-mock.md +50 -0
  394. package/templates/skills/orc-pact/SKILL.md +218 -0
  395. package/templates/skills/orc-pact/references/gate.md +70 -0
  396. package/templates/skills/orc-pact/references/ledger.md +99 -0
  397. package/templates/skills/orc-pattern/SKILL.md +124 -0
  398. package/templates/skills/orc-pattern/references/INDEX.md +73 -0
  399. package/templates/skills/orc-pattern/references/be-django.md +80 -0
  400. package/templates/skills/orc-pattern/references/be-express.md +82 -0
  401. package/templates/skills/orc-pattern/references/be-fastapi.md +107 -0
  402. package/templates/skills/orc-pattern/references/be-go.md +96 -0
  403. package/templates/skills/orc-pattern/references/be-nestjs.md +86 -0
  404. package/templates/skills/orc-pattern/references/be-postgres.md +98 -0
  405. package/templates/skills/orc-pattern/references/fe-a11y.md +36 -0
  406. package/templates/skills/orc-pattern/references/fe-angular.md +87 -0
  407. package/templates/skills/orc-pattern/references/fe-nextjs.md +65 -0
  408. package/templates/skills/orc-pattern/references/fe-perf.md +39 -0
  409. package/templates/skills/orc-pattern/references/fe-react.md +83 -0
  410. package/templates/skills/orc-pattern/references/fe-vue.md +75 -0
  411. package/templates/skills/orc-pattern/schemas/pattern-doc.md +62 -0
  412. package/templates/skills/orc-poly/SKILL.md +216 -0
  413. package/templates/skills/orc-poly/examples/poly-run-mock.md +51 -0
  414. package/templates/skills/orc-poly/references/gather.md +70 -0
  415. package/templates/skills/orc-poly/references/poly-spec.md +84 -0
  416. package/templates/skills/orc-pr-driver/README.md +349 -0
  417. package/templates/skills/orc-pr-driver/SKILL.md +167 -0
  418. package/templates/skills/orc-pr-driver/references/conflict-playbook.md +78 -0
  419. package/templates/skills/orc-pr-driver/references/green-gate.md +105 -0
  420. package/templates/skills/orc-pr-driver/references/orc-run-split.md +99 -0
  421. package/templates/skills/orc-pr-setup/README.md +436 -0
  422. package/templates/skills/orc-pr-setup/SKILL.md +184 -0
  423. package/templates/skills/orc-pr-setup/references/certainty-gate.md +66 -0
  424. package/templates/skills/orc-pr-setup/references/layer-taxonomy.md +67 -0
  425. package/templates/skills/orc-quick/README.md +423 -0
  426. package/templates/skills/orc-quick/SKILL.md +306 -0
  427. package/templates/skills/orc-quick/references/context-doc.md +114 -0
  428. package/templates/skills/orc-quick/references/dispatch-gate.md +163 -0
  429. package/templates/skills/orc-quick/references/gh-mode.md +127 -0
  430. package/templates/skills/orc-retro/SKILL.md +222 -0
  431. package/templates/skills/orc-retro/examples/retro-mock.md +171 -0
  432. package/templates/skills/orc-route/SKILL.md +165 -0
  433. package/templates/skills/orc-verify/SKILL.md +86 -0
  434. package/templates/skills/orc-verify/examples/verify-mock.md +33 -0
  435. package/templates/skills/orc-wiki/README.md +325 -0
  436. package/templates/skills/orc-wiki/SKILL.md +322 -0
  437. package/templates/skills/orc-wiki/examples/wiki-run-mock.md +37 -0
  438. package/templates/skills/orc-wiki/references/claude-md-injection.md +61 -0
  439. package/templates/skills/orc-wiki/references/crosslink-compile.md +39 -0
  440. package/templates/skills/orc-wiki/references/crosslink-kinds.md +57 -0
  441. package/templates/skills/orc-wiki/references/crosslink.md +284 -0
  442. package/templates/skills/orc-wiki/references/extra.md +79 -0
  443. package/templates/skills/orc-wiki/references/integrity-check.md +86 -0
  444. package/templates/skills/orc-wiki/references/orientation.md +51 -0
  445. package/templates/skills/orc-wiki/references/partial-refresh.md +203 -0
  446. package/templates/skills/orc-wiki/references/pattern-prewarm.md +19 -0
  447. package/templates/skills/orc-wiki/references/staleness.md +288 -0
  448. package/templates/skills/orc-wiki/schemas/crosslink-tag.md +166 -0
  449. package/templates/skills/orc-wiki/schemas/wiki-doc.md +120 -0
@@ -0,0 +1,654 @@
1
+ "use strict";
2
+ /* panels/crosslink.js — orc ui client
3
+ Design and Settings. VAULT.W/H must stay the same box as .vault-node's CSS
4
+ width/height — a test asserts it and re-runs ringRadii over n=1..16.
5
+
6
+ Loaded by app.html in the order its numeric prefix names. Classic
7
+ script, no import/export: an ES module import carries no query string,
8
+ and every static request here needs the per-launch session token. */
9
+
10
+ /* =============================================================== CROSSLINK == */
11
+
12
+ PANELS.crosslink = function (host) {
13
+ head(host, t("crosslink.title"), t("crosslink.sub"));
14
+ const body = el("div", "stack");
15
+ host.append(body);
16
+ renderCrosslink(body);
17
+ };
18
+
19
+ // TWO TABS (v0.43.7): DESIGN is the picture of the boundary, SETTINGS is every
20
+ // control. They were one scrolling column, which made the diagram something you
21
+ // scrolled past on the way to the add form rather than the thing you came for.
22
+ //
23
+ // With nothing linked yet there is no picture to draw, so Settings opens
24
+ // selected and its tab is spotlighted — Design stays reachable and says, in the
25
+ // empty state, exactly which tab makes it appear.
26
+ async function renderCrosslink(body) {
27
+ body.replaceChildren(skeleton(5));
28
+ const d = (await read("/api/crosslink")).data;
29
+ const live = d.configured && d.nodes.length > 0;
30
+
31
+ const tabs = el("div", "tabs");
32
+ // `stack` as well as `tab-pane`: the Settings tab holds several cards, and
33
+ // the container is what spaces panel blocks — see `.stack` in app.css.
34
+ const pane = el("div", "tab-pane stack");
35
+ const views = {
36
+ design: () => designView(d, live, () => select("settings")),
37
+ settings: () => settingsView(d, body),
38
+ };
39
+ const select = async (which) => {
40
+ for (const b of tabs.children) b.setAttribute("aria-selected", String(b.dataset.tab === which));
41
+ pane.replaceChildren(skeleton(3));
42
+ const built = await views[which]();
43
+ pane.replaceChildren(built);
44
+ };
45
+ // Keys are written out in full, never assembled from the tab id — a key built
46
+ // from a fragment is invisible to every check that looks for one.
47
+ for (const [which, label] of [["design", t("crosslink.tab.design")], ["settings", t("crosslink.tab.settings")]]) {
48
+ const b = el("button", null, label);
49
+ b.type = "button";
50
+ b.dataset.tab = which;
51
+ // The spotlight is the answer to "there is nothing here" — it points at the
52
+ // one tab that can change that, and it is dropped the moment a link exists.
53
+ if (!live && which === "settings") b.classList.add("tab-spot");
54
+ b.addEventListener("click", () => select(which));
55
+ tabs.append(b);
56
+ }
57
+ body.replaceChildren(tabs, pane);
58
+ select(live ? "design" : "settings");
59
+ }
60
+
61
+ function designView(d, live, gotoSettings) {
62
+ const out = frag();
63
+ if (!live) {
64
+ const e = empty(t("crosslink.design.empty"), t("crosslink.design.emptyHint"));
65
+ const go = el("button", "btn btn-sm btn-primary", t("crosslink.design.emptyCta"));
66
+ go.type = "button";
67
+ go.addEventListener("click", gotoSettings);
68
+ e.append(go);
69
+ out.append(e);
70
+ return out;
71
+ }
72
+ out.append(vaultCard(d));
73
+ return out;
74
+ }
75
+
76
+ async function settingsView(d, body) {
77
+ const out = frag();
78
+
79
+ if (!d.configured) {
80
+ out.append(empty(t("crosslink.empty"), t("crosslink.emptyHint")));
81
+ out.append(await addLinkCard(d, body));
82
+ return out;
83
+ }
84
+
85
+ const head2 = card(t("crosslink.graph"));
86
+ head2.append(
87
+ kvList(
88
+ [
89
+ [t("crosslink.self"), d.self],
90
+ [t("crosslink.config"), d.config_path],
91
+ [t("crosslink.needs"), d.needs_baseline || t("crosslink.needsNone")],
92
+ ],
93
+ true
94
+ )
95
+ );
96
+ out.append(head2);
97
+
98
+ out.append(await addLinkCard(d, body));
99
+
100
+ for (const n of d.nodes) {
101
+ const c = el("div", "action");
102
+ c.dataset.node = n.name; // paired with the graph node above on hover
103
+ const left = el("div");
104
+ const name = el("div", "setting-name");
105
+ name.append(document.createTextNode(n.name));
106
+ name.append(
107
+ chip(
108
+ n.direction === "consume" ? t("crosslink.weCall") : n.direction === "provide" ? t("crosslink.theyCall") : t("crosslink.noEdge"),
109
+ n.direction === "consume" ? "info" : n.direction === "provide" ? "" : "warn"
110
+ )
111
+ );
112
+ left.append(name);
113
+ // repo_path and kind ids come straight from the config file.
114
+ left.append(el("div", "setting-desc", n.repo_path + (n.kinds.length ? " · kinds: " + n.kinds.join(", ") : "")));
115
+ const pv = n.provider || {};
116
+ if (pv.state === "missing") left.append(el("div", "note", t("crosslink.state.missing")));
117
+ else if (pv.state === "no-wiki") left.append(el("div", "note", t("crosslink.state.noWiki")));
118
+ else if (pv.state === "unregistered") left.append(el("div", "note", t("crosslink.state.unregistered")));
119
+ else if (pv.state === "corrupt") left.append(el("div", "note", t("crosslink.state.corrupt")));
120
+ else if (n.direction === "provide") left.append(el("div", "note", t("crosslink.state.inbound")));
121
+ else {
122
+ const row = el("div", "row-actions");
123
+ row.append(chip(pv.tier || t("overview.tile.wikiUnknown"), pv.tier === "FRESH" ? "ok" : pv.tier === "AGING" ? "warn" : "bad", pv.tier === "STALE"));
124
+ row.append(el("span", "note", t("crosslink.peerNote", { scan: pv.last_scan || "?", tags: pv.tags || 0 })));
125
+ left.append(row);
126
+ if (!pv.tags) left.append(el("div", "note", t("crosslink.noTags", { path: n.repo_path })));
127
+ }
128
+ const rm = el("button", "btn btn-sm btn-danger", t("common.remove"));
129
+ rm.type = "button";
130
+ rm.addEventListener("click", () => confirmRemove(n.name, body));
131
+ c.append(left, rm);
132
+ out.append(c);
133
+ }
134
+
135
+ if (d.links.length) {
136
+ const lc = card(t("crosslink.edges"));
137
+ for (const l of d.links) lc.append(el("div", "note", `${l.from} ──${l.via}──▶ ${l.to} (${l.relation.replace(/-/g, " ")})`));
138
+ out.append(lc);
139
+ }
140
+
141
+ return out;
142
+ }
143
+
144
+ /* ------------------------------------------------------- the vault graph -- */
145
+
146
+ // THE DESIGN TAB (v0.43.7). Self in the middle, every linked repo on a ring
147
+ // around it, one line per edge drawn in the direction it actually points, and a
148
+ // pulse travelling that line so "which way does this one go" is answered by
149
+ // motion instead of by reading an arrow glyph.
150
+ //
151
+ // The layout is COMPUTED, not simulated: peer i sits at a fixed angle for a
152
+ // given node count, so the same config draws the same picture every time you
153
+ // open the tab. A physics sim would be prettier to poke and useless to compare.
154
+ //
155
+ // THE LAYOUT IS SOLVED IN PIXELS FROM THE REAL BOX SIZE, and that is the whole
156
+ // fix in v0.43.7's first patch. Placing fixed-pixel boxes on a ring whose radius
157
+ // was a FRACTION of the container ("0.34 of the height") is a guess: nothing in
158
+ // it knows how wide a repo box is, so at three peers the boxes overlapped and
159
+ // the picture was unreadable. Repo boxes are a fixed size now and the radii are
160
+ // derived from that size, so separation is a property of the layout rather than
161
+ // a value that happened to look right in one screenshot. See ringRadii().
162
+ //
163
+ // Edges carry pathLength="1", so the draw-in and the travelling pulse are
164
+ // written as fractions of the line and never need its length measured.
165
+
166
+ // Fixed box metrics. Fixed is what makes the geometry solvable — a box that
167
+ // sizes to its longest repo name cannot be spaced by arithmetic, only measured
168
+ // and re-measured. Long names ellipsis instead.
169
+ const VAULT = { W: 156, H: 78, GAP: 20, PAD: 24 };
170
+
171
+ // The smallest ellipse on which no two boxes — and no box and the hub — can
172
+ // overlap.
173
+ //
174
+ // Two axis-aligned boxes miss each other when |dx| >= W+GAP OR |dy| >= H+GAP.
175
+ // For neighbours Δ apart on the ring with midangle m:
176
+ // dx = rx·2sin(Δ/2)·|sin m| dy = ry·2sin(Δ/2)·|cos m|
177
+ // One of |sin m|, |cos m| is always at least 1/√2, so scaling both radii by √2
178
+ // makes whichever term is doing the work clear its threshold on its own. The
179
+ // same √2 as a FLOOR handles the hub, which sits at the centre and is a box too.
180
+ // Wider angular gaps only push boxes further apart, so neighbours are the worst
181
+ // case and checking them is enough.
182
+ function ringRadii(n) {
183
+ const needX = Math.SQRT2 * (VAULT.W + VAULT.GAP);
184
+ const needY = Math.SQRT2 * (VAULT.H + VAULT.GAP);
185
+ // n === 1 has no neighbour pair — and sin(π/1) is 0, so the general form
186
+ // divides by zero. One peer only ever has to clear the hub.
187
+ const sep = n > 1 ? 2 * Math.sin(Math.PI / n) : Infinity;
188
+ return { rx: Math.max(needX, needX / sep), ry: Math.max(needY, needY / sep) };
189
+ }
190
+
191
+ function vaultCard(d) {
192
+ const summary = t(d.nodes.length === 1 && d.links.length === 1 ? "crosslink.repos" : "crosslink.reposPlural", {
193
+ repos: d.nodes.length,
194
+ edges: d.links.length,
195
+ });
196
+ const c = card(t("crosslink.design.title"), chip(summary, "info"));
197
+ c.append(el("div", "note", t("crosslink.design.note")));
198
+
199
+ const n = d.nodes.length;
200
+ const { rx, ry } = ringRadii(n);
201
+ // Lay out around an origin first, then size the canvas to what was actually
202
+ // placed. Deriving it from the radii instead would pad for a full ellipse even
203
+ // when the ring only uses part of one — a single peer would sit in a box wide
204
+ // enough for six. The ring opens at -90°, so peer 0 is directly above.
205
+ const at = [[0, 0]]; // the hub
206
+ for (let i = 0; i < n; i++) {
207
+ const a = -Math.PI / 2 + (i * 2 * Math.PI) / n;
208
+ at.push([rx * Math.cos(a), ry * Math.sin(a)]);
209
+ }
210
+ const half = [VAULT.W / 2, VAULT.H / 2];
211
+ const bound = (axis) => {
212
+ const lo = Math.min(...at.map((p) => p[axis] - half[axis]));
213
+ const hi = Math.max(...at.map((p) => p[axis] + half[axis]));
214
+ return [lo, hi - lo];
215
+ };
216
+ const [minX, spanX] = bound(0);
217
+ const [minY, spanY] = bound(1);
218
+ const boxW = Math.round(spanX + 2 * VAULT.PAD);
219
+ const boxH = Math.round(spanY + 2 * VAULT.PAD);
220
+ // Origin-relative → canvas pixels.
221
+ const put = (p) => [p[0] - minX + VAULT.PAD, p[1] - minY + VAULT.PAD];
222
+
223
+ // Positions are pixel centres. `self` and the repo's real name both map to the
224
+ // hub because a link may name this repo either way.
225
+ const hub = put(at[0]);
226
+ const pos = { [d.self]: hub, self: hub };
227
+ d.nodes.forEach((node, i) => {
228
+ pos[node.name] = put(at[i + 1]);
229
+ });
230
+
231
+ // A ring big enough not to collide can be wider than the panel. That is a
232
+ // scroll, not a reason to squeeze the boxes back together.
233
+ const scroll = el("div", "scroll-x");
234
+ const wrap = el("div", "vault");
235
+ wrap.style.width = boxW + "px";
236
+ wrap.style.height = boxH + "px";
237
+
238
+ const svg = svgEl("svg", { class: "vault-edges", viewBox: `0 0 ${boxW} ${boxH}` });
239
+ d.links.forEach((l, i) => {
240
+ // `from`/`to` may name this repo either as the literal "self" or by its
241
+ // real name — the config accepts both, so the position map holds both.
242
+ const a = pos[l.from];
243
+ const b = pos[l.to];
244
+ if (!a || !b) return; // an edge naming a repo that is not in the graph
245
+ const g = svgEl("g", { class: "vault-edge", "data-a": l.from, "data-b": l.to });
246
+ g.style.setProperty("--d", i * 140 + "ms");
247
+ // The canvas is a known pixel size, so the viewBox is 1:1 with it and the
248
+ // endpoints go straight in — no measuring pass, and no stretched viewBox
249
+ // squashing the labels and strokes.
250
+ const ends = { x1: a[0].toFixed(1), y1: a[1].toFixed(1), x2: b[0].toFixed(1), y2: b[1].toFixed(1), pathLength: "1" };
251
+ g.append(svgEl("line", { ...ends, class: "vault-line" }));
252
+ // The pulse runs from → to, which IS the direction of the dependency.
253
+ g.append(svgEl("line", { ...ends, class: "vault-pulse" }));
254
+ if (l.via) {
255
+ // Nudged off the line so the label sits beside it, not on top of it.
256
+ const label = svgEl("text", {
257
+ class: "vault-via",
258
+ x: ((a[0] + b[0]) / 2).toFixed(1),
259
+ y: ((a[1] + b[1]) / 2 - 8).toFixed(1),
260
+ "text-anchor": "middle",
261
+ });
262
+ label.textContent = l.via;
263
+ g.append(label);
264
+ }
265
+ svg.append(g);
266
+ });
267
+ wrap.append(svg);
268
+
269
+ const place = (node, p, i) => {
270
+ node.style.left = p[0].toFixed(1) + "px";
271
+ node.style.top = p[1].toFixed(1) + "px";
272
+ node.style.setProperty("--d", i * 90 + "ms");
273
+ wrap.append(node);
274
+ };
275
+
276
+ const self = el("div", "vault-node vault-hub");
277
+ self.append(el("span", "vault-name", d.self), el("span", "vault-sub", t("crosslink.design.thisRepo")));
278
+ place(self, pos[d.self], 0);
279
+
280
+ d.nodes.forEach((node, i) => {
281
+ const box = el("button", "vault-node vault-" + (node.direction || "none"));
282
+ box.type = "button";
283
+ box.dataset.name = node.name;
284
+ box.append(el("span", "vault-name", node.name));
285
+ box.append(el("span", "vault-sub", node.repo_path));
286
+ const pv = node.provider || {};
287
+ // The chip carries the CLI's own state word, so the picture and `orc
288
+ // crosslink list` always say the same thing.
289
+ const state =
290
+ pv.state === "missing" ? ["missing", "bad"] :
291
+ pv.state === "no-wiki" ? ["no wiki", "warn"] :
292
+ pv.state === "unregistered" ? ["unregistered", "warn"] :
293
+ pv.state === "corrupt" ? ["corrupt", "bad"] :
294
+ node.direction === "provide" ? ["inbound", ""] :
295
+ [pv.tier || "linked", pv.tier === "FRESH" ? "ok" : pv.tier === "AGING" ? "warn" : "bad"];
296
+ box.append(chip(state[0], state[1]));
297
+ box.title = `${node.name} — ${node.repo_path}`;
298
+
299
+ // Hovering a repo lights the edges it is an end of, so a busy ring can still
300
+ // be read one repo at a time.
301
+ const hi = (onOff) => {
302
+ box.classList.toggle("vault-hi", onOff);
303
+ for (const g of svg.querySelectorAll(".vault-edge")) {
304
+ if (g.dataset.a === node.name || g.dataset.b === node.name) g.classList.toggle("edge-hi", onOff);
305
+ else g.classList.toggle("edge-dim", onOff);
306
+ }
307
+ };
308
+ box.addEventListener("mouseenter", () => hi(true));
309
+ box.addEventListener("mouseleave", () => hi(false));
310
+ box.addEventListener("focus", () => hi(true));
311
+ box.addEventListener("blur", () => hi(false));
312
+ // Clicking a repo is a question about that repo — the answer (its state, its
313
+ // kinds, the Remove button) lives on the Settings tab, so go there.
314
+ box.addEventListener("click", () => {
315
+ const tab = document.querySelector('.tabs button[data-tab="settings"]');
316
+ if (!tab) return;
317
+ tab.click();
318
+ setTimeout(() => {
319
+ const row = document.querySelector('.action[data-node="' + node.name + '"]');
320
+ if (!row) return;
321
+ row.scrollIntoView({ behavior: "smooth", block: "center" });
322
+ row.classList.add("linked-hi");
323
+ setTimeout(() => row.classList.remove("linked-hi"), 1600);
324
+ }, 120);
325
+ });
326
+ place(box, pos[node.name], i + 1);
327
+ });
328
+
329
+ scroll.append(wrap);
330
+ c.append(scroll);
331
+ return c;
332
+ }
333
+
334
+ function svgEl(tag, attrs) {
335
+ const node = document.createElementNS("http://www.w3.org/2000/svg", tag);
336
+ for (const [k, v] of Object.entries(attrs || {})) node.setAttribute(k, v);
337
+ return node;
338
+ }
339
+
340
+ // The add form. It mirrors the interactive CLI prompt field for field, and it
341
+ // submits to the CLI rather than writing YAML — so the errors shown here are
342
+ // the CLI's own, not a second opinion about what is valid.
343
+ async function addLinkCard(d, body) {
344
+ const c = card(t("crosslink.add.title"));
345
+ c.append(el("div", "note", t("crosslink.add.note")));
346
+
347
+ const form = el("div", "linkform");
348
+ const mk = (labelText, node, hint) => {
349
+ // `label` wraps its control, and a label that CONTAINS a button steals that
350
+ // button's click — so the path row (input + Browse) is a plain div instead.
351
+ const f = el(node.dataset && node.dataset.nolabel ? "div" : "label", "field");
352
+ f.append(el("span", "field-label", labelText));
353
+ f.append(node);
354
+ if (hint) f.append(el("span", "field-hint", hint));
355
+ return f;
356
+ };
357
+
358
+ const name = el("input", "text-input");
359
+ name.placeholder = "service-z";
360
+
361
+ // THE PATH FIELD (v0.43.6). A hand-typed path is the one field here whose
362
+ // mistakes are invisible: the CLI accepts an unresolvable path on purpose
363
+ // (it saves a PENDING edge that resolves later), so a typo does not fail —
364
+ // it just silently never links. The Browse button removes the typo entirely.
365
+ // It is still a plain text input: browsing is an ADDITION, never the only
366
+ // way in, and pasting a path you already know stays the fastest route.
367
+ const repoPath = el("input", "text-input");
368
+ repoPath.placeholder = "../service-z";
369
+ const pathRow = el("div", "path-row");
370
+ pathRow.dataset.nolabel = "1";
371
+ const browse = el("button", "btn btn-sm", t("crosslink.add.browse"));
372
+ browse.type = "button";
373
+ browse.addEventListener("click", () =>
374
+ pickFolder((rel) => {
375
+ repoPath.value = rel;
376
+ syncCmd();
377
+ })
378
+ );
379
+ pathRow.append(repoPath, browse);
380
+
381
+ // The catalog comes from the CLI so the picker can never drift from it.
382
+ let kinds = [];
383
+ try {
384
+ kinds = (await read("/api/crosslink/kinds")).data.kinds || [];
385
+ } catch (_) {}
386
+ const kindBox = el("div", "kind-picker");
387
+ const picked = new Set();
388
+ for (const k of kinds) {
389
+ const b = el("button", "kind", k);
390
+ b.type = "button";
391
+ b.addEventListener("click", () => {
392
+ if (picked.has(k)) picked.delete(k);
393
+ else picked.add(k);
394
+ b.classList.toggle("kind-on", picked.has(k));
395
+ syncVia();
396
+ });
397
+ kindBox.append(b);
398
+ }
399
+ const custom = el("input", "text-input");
400
+ custom.placeholder = t("crosslink.add.customHint");
401
+
402
+ const dirSeg = el("div", "seg");
403
+ let direction = "calls";
404
+ const self = d.self || "this repo";
405
+ const dirs = [
406
+ ["calls", t("crosslink.add.dirCalls", { self })],
407
+ ["called-by", t("crosslink.add.dirCalledBy", { self })],
408
+ ];
409
+ for (const [val, label] of dirs) {
410
+ const b = el("button", null, label);
411
+ b.type = "button";
412
+ b.setAttribute("aria-pressed", String(val === direction));
413
+ b.addEventListener("click", () => {
414
+ direction = val;
415
+ for (const other of dirSeg.children) other.setAttribute("aria-pressed", "false");
416
+ b.setAttribute("aria-pressed", "true");
417
+ });
418
+ dirSeg.append(b);
419
+ }
420
+
421
+ const via = el("select", "text-input");
422
+ const syncVia = () => {
423
+ const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
424
+ via.replaceChildren();
425
+ for (const k of [...new Set(all)]) {
426
+ const o = el("option", null, k);
427
+ o.value = k;
428
+ via.append(o);
429
+ }
430
+ via.disabled = !all.length;
431
+ };
432
+ custom.addEventListener("input", syncVia);
433
+ syncVia();
434
+
435
+ form.append(
436
+ mk(t("crosslink.add.name"), name, t("crosslink.add.nameHint")),
437
+ mk(t("crosslink.add.path"), pathRow, t("crosslink.add.pathHint")),
438
+ mk(t("crosslink.add.kinds"), kindBox, t("crosslink.add.kindsHint")),
439
+ mk(t("crosslink.add.custom"), custom),
440
+ mk(t("crosslink.add.direction"), dirSeg, t("crosslink.add.directionHint")),
441
+ mk(t("crosslink.add.via"), via, t("crosslink.add.viaHint"))
442
+ );
443
+ c.append(form);
444
+
445
+ const err = el("div", "input-err");
446
+ const actions = el("div", "row-actions");
447
+ const save = el("button", "btn btn-primary", t("crosslink.add.save"));
448
+ save.type = "button";
449
+ const cmdPreview = el("code", "action-cmd", "");
450
+ const syncCmd = () => {
451
+ const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
452
+ cmdPreview.textContent =
453
+ `orc crosslink add ${name.value || "<name>"} ${repoPath.value || "<path>"} ` +
454
+ `--kinds ${all.join(",") || "<kinds>"} --direction ${direction}` +
455
+ (via.value ? ` --via ${via.value}` : "");
456
+ };
457
+ for (const n of [name, repoPath, custom]) n.addEventListener("input", syncCmd);
458
+ via.addEventListener("change", syncCmd);
459
+ kindBox.addEventListener("click", syncCmd);
460
+ dirSeg.addEventListener("click", syncCmd);
461
+ syncCmd();
462
+
463
+ save.addEventListener("click", async () => {
464
+ err.textContent = "";
465
+ const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
466
+ // Only the empties are caught here. Everything about VALIDITY — the slug
467
+ // shape, a taken name, an unknown target — is the CLI's call, reported below.
468
+ if (!name.value.trim() || !repoPath.value.trim() || !all.length) {
469
+ err.textContent = t("crosslink.add.required");
470
+ return;
471
+ }
472
+ save.disabled = true;
473
+ save.textContent = t("crosslink.add.saving");
474
+ try {
475
+ const r = await post("/api/crosslink/add", {
476
+ name: name.value.trim(),
477
+ repo_path: repoPath.value.trim(),
478
+ kinds: all.join(","),
479
+ direction,
480
+ via: via.value || all[0],
481
+ });
482
+ if (!r.ok) {
483
+ // The CLI's own rejection text wins over ours whenever there is one.
484
+ err.textContent = r.output || t("crosslink.add.refused");
485
+ save.disabled = false;
486
+ save.textContent = t("crosslink.add.save");
487
+ return;
488
+ }
489
+ toast(t("crosslink.add.linked", { name: name.value.trim() }), "ok", r.output);
490
+ await renderCrosslink(body);
491
+ // The new node draws itself in — the one moment where motion is the
492
+ // feedback that the link now exists.
493
+ const fresh = document.querySelector('.graph-node[data-name="' + name.value.trim() + '"]');
494
+ if (fresh) fresh.classList.add("graph-new");
495
+ } catch (e) {
496
+ err.textContent = String(e.message);
497
+ save.disabled = false;
498
+ save.textContent = t("crosslink.add.save");
499
+ }
500
+ });
501
+ actions.append(save);
502
+ c.append(cmdPreview, err, actions);
503
+ return c;
504
+ }
505
+
506
+ /* --- the folder picker ------------------------------------------------------
507
+ A browser cannot hand back a real filesystem path — `<input type="file"
508
+ webkitdirectory>` gives a folder NAME and nothing above it, which is exactly
509
+ the part a relative repo path needs. So the picker walks the filesystem on
510
+ the SERVER (`/api/fs/list`, directory names only) and the browser just
511
+ renders it. That also makes it identical on Windows and macOS: the server
512
+ knows the real separator and computes the stored relative path itself, so
513
+ nothing here has to guess whether to write `..\peer` or `../peer`.
514
+
515
+ It never picks a FILE and never opens one. `onPick` receives the relative
516
+ path the crosslink config will store — the same string you would have typed. */
517
+ function pickFolder(onPick) {
518
+ const body = el("div", "stack stack-sm");
519
+ const crumbs = el("div", "picker-crumbs");
520
+ const listBox = el("div", "picker-list");
521
+ const foot = el("div", "picker-foot");
522
+ const relLine = el("div", "note");
523
+ body.append(crumbs, listBox, relLine, foot);
524
+
525
+ let current = null; // the listing payload for the folder on screen
526
+
527
+ const choose = el("button", "btn btn-primary btn-allow-busy", t("picker.choose"));
528
+ choose.type = "button";
529
+ choose.disabled = true;
530
+
531
+ const go = async (path) => {
532
+ listBox.replaceChildren(skeleton(6));
533
+ let d;
534
+ try {
535
+ d = (await read("/api/fs/list" + (path ? "?path=" + encodeURIComponent(path) : ""))).data;
536
+ } catch (e) {
537
+ listBox.replaceChildren(empty(t("picker.unreadable"), String(e.message)));
538
+ return;
539
+ }
540
+ current = d;
541
+
542
+ // Breadcrumbs: the shortcuts you actually want (up, home, this project)
543
+ // rather than a clickable path split on the separator, which on Windows is
544
+ // a row of one-letter targets.
545
+ crumbs.replaceChildren();
546
+ const crumb = (label, target, disabled) => {
547
+ const b = el("button", "btn btn-ghost btn-sm", label);
548
+ b.type = "button";
549
+ b.disabled = !target || disabled;
550
+ b.addEventListener("click", () => go(target));
551
+ return b;
552
+ };
553
+ crumbs.append(crumb("↑ " + t("picker.up"), d.parent));
554
+ crumbs.append(crumb(t("picker.home"), d.home));
555
+ if (d.project_root) crumbs.append(crumb(t("picker.project"), d.project_root));
556
+ crumbs.append(el("code", "picker-path", d.path));
557
+
558
+ listBox.replaceChildren();
559
+ if (d.error) {
560
+ listBox.append(empty(t("picker.unreadable"), d.error));
561
+ } else if (!d.dirs.length) {
562
+ listBox.append(empty(t("picker.empty")));
563
+ } else {
564
+ for (const dir of d.dirs) {
565
+ const row = el("button", "picker-item");
566
+ row.type = "button";
567
+ row.append(el("span", "picker-icon", dir.is_repo ? "◆" : "▸"));
568
+ const mid = el("div");
569
+ mid.append(el("div", "picker-name", dir.name));
570
+ const tags = el("div", "picker-tags");
571
+ // The two facts that decide whether linking this folder is useful.
572
+ if (dir.is_repo) tags.append(chip(t("picker.isRepo"), "info"));
573
+ if (dir.has_wiki) tags.append(chip(t("picker.hasWiki"), "ok"));
574
+ if (d.project_root && dir.path === d.project_root) tags.append(chip(t("picker.sameRepo"), "warn"));
575
+ mid.append(tags);
576
+ row.append(mid, el("span", "picker-into", "→"));
577
+ // Single click NAVIGATES into the folder; "Use this folder" selects the
578
+ // one you are standing in. One gesture per meaning, so a click never
579
+ // both descends and commits.
580
+ row.addEventListener("click", () => go(dir.path));
581
+ listBox.append(row);
582
+ }
583
+ }
584
+
585
+ const isSelf = d.is_project_root;
586
+ choose.disabled = !!d.error || isSelf;
587
+ relLine.textContent = d.error
588
+ ? ""
589
+ : isSelf
590
+ ? t("picker.sameRepo")
591
+ : t("picker.relative", { rel: d.relative });
592
+ relLine.classList.toggle("picker-warn", isSelf);
593
+ };
594
+
595
+ choose.addEventListener("click", () => {
596
+ if (!current || current.is_project_root) return;
597
+ onPick(current.relative);
598
+ close();
599
+ });
600
+ foot.append(choose);
601
+
602
+ const close = modal({
603
+ title: t("picker.title"),
604
+ body,
605
+ actions: [{ label: t("common.cancel"), onClick: (c) => c() }],
606
+ });
607
+ body.insertBefore(el("div", "note", t("picker.note")), crumbs);
608
+ // Start one level ABOVE the project: a linked repo is almost always a sibling.
609
+ go(metaInfo.project_root ? metaInfo.project_root + "/.." : "");
610
+ return close;
611
+ }
612
+
613
+ function confirmRemove(name, body) {
614
+ const b = el("div");
615
+ b.append(el("div", null, t("crosslink.remove.body", { name })));
616
+ b.append(el("div", "note", t("crosslink.remove.note")));
617
+ b.append(el("div", "action-cmd", `orc crosslink remove ${name}`));
618
+ const close = modal({
619
+ title: t("crosslink.remove.title"),
620
+ body: b,
621
+ actions: [
622
+ { label: t("common.cancel"), onClick: (c) => c() },
623
+ {
624
+ label: t("common.remove"),
625
+ cls: "btn-danger",
626
+ onClick: async (c) => {
627
+ const r = await post("/api/crosslink/remove", { name });
628
+ toast(r.command, r.ok ? "ok" : "bad", r.output);
629
+ c();
630
+ renderCrosslink(body);
631
+ },
632
+ },
633
+ ],
634
+ });
635
+ return close;
636
+ }
637
+
638
+ /* =================================================================== LEARN == */
639
+
640
+ /* ONE THING AT A TIME (v0.43.6).
641
+ This panel used to stack all eight walkthrough sections as eight boxes of
642
+ monospace text, every one of them open. That is the whole document dumped on
643
+ screen: nothing is emphasised, so nothing is read, and finding the section
644
+ you wanted meant scrolling through the seven you did not.
645
+
646
+ A walkthrough has a natural shape — it is ORDERED, and you are at a position
647
+ in it. So the panel now shows a CONTENTS rail and exactly one section, with
648
+ Previous / Next, a progress bar, and a search that filters the rail rather
649
+ than the page. The content is unchanged: it is the same `bin/onboarding-
650
+ content.js` the terminal prints, which is the point — one source, two
651
+ surfaces.
652
+
653
+ Where you are is remembered per browser, so switching panels and coming back
654
+ does not restart the walkthrough. */