dsh-compound-engineering 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (461) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -2
  3. package/README.zh.md +161 -0
  4. package/cordis.patch.yml +6 -0
  5. package/lib/frontmatter.js +167 -0
  6. package/lib/index.js +46 -0
  7. package/lib/provider.js +201 -0
  8. package/package.json +62 -4
  9. package/skills/CODING_STANDARDS.md +68 -0
  10. package/skills/ce-babysit-pr/SKILL.md +59 -0
  11. package/skills/ce-babysit-pr/references/branch-currency.md +22 -0
  12. package/skills/ce-babysit-pr/references/envelope.md +44 -0
  13. package/skills/ce-babysit-pr/references/pipeline.md +15 -0
  14. package/skills/ce-babysit-pr/references/report.md +20 -0
  15. package/skills/ce-babysit-pr/references/settle.md +37 -0
  16. package/skills/ce-babysit-pr/references/setup.md +33 -0
  17. package/skills/ce-babysit-pr/references/stack-commands.md +39 -0
  18. package/skills/ce-babysit-pr/references/stack.md +43 -0
  19. package/skills/ce-babysit-pr/references/tick.md +102 -0
  20. package/skills/ce-babysit-pr/references/watch-loop.md +222 -0
  21. package/skills/ce-babysit-pr/scripts/pr-snapshot +3656 -0
  22. package/skills/ce-bakeoff/SKILL.md +48 -0
  23. package/skills/ce-bakeoff/references/candidates.md +37 -0
  24. package/skills/ce-bakeoff/references/judging.md +19 -0
  25. package/skills/ce-bakeoff/references/output.md +11 -0
  26. package/skills/ce-bakeoff/references/verification.md +15 -0
  27. package/skills/ce-brainstorm/SKILL.md +69 -0
  28. package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
  29. package/skills/ce-brainstorm/references/approaches.md +57 -0
  30. package/skills/ce-brainstorm/references/bakeoff.md +9 -0
  31. package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
  32. package/skills/ce-brainstorm/references/brainstorm-sections.md +458 -0
  33. package/skills/ce-brainstorm/references/dialogue.md +89 -0
  34. package/skills/ce-brainstorm/references/handoff.md +187 -0
  35. package/skills/ce-brainstorm/references/html-rendering.md +638 -0
  36. package/skills/ce-brainstorm/references/interaction-rules.md +33 -0
  37. package/skills/ce-brainstorm/references/markdown-rendering.md +242 -0
  38. package/skills/ce-brainstorm/references/model-tiers.md +13 -0
  39. package/skills/ce-brainstorm/references/output-mode.md +26 -0
  40. package/skills/ce-brainstorm/references/phase-0.md +104 -0
  41. package/skills/ce-brainstorm/references/plan-write.md +32 -0
  42. package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
  43. package/skills/ce-brainstorm/references/reasoning-elevation.md +124 -0
  44. package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
  45. package/skills/ce-brainstorm/references/synthesis-summary.md +149 -0
  46. package/skills/ce-brainstorm/references/universal-brainstorming.md +72 -0
  47. package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
  48. package/skills/ce-brainstorm/references/visual-probes.md +164 -0
  49. package/skills/ce-brainstorm/scripts/elevation-dispatch.sh +288 -0
  50. package/skills/ce-brainstorm/scripts/light-webserver.js +1275 -0
  51. package/skills/ce-brainstorm/scripts/packs-resolve.py +737 -0
  52. package/skills/ce-brainstorm/scripts/peer-job-runner.py +2594 -0
  53. package/skills/ce-code-review/SKILL.md +40 -0
  54. package/skills/ce-code-review/references/action-class-rubric.md +67 -0
  55. package/skills/ce-code-review/references/cross-model-recovery.md +38 -0
  56. package/skills/ce-code-review/references/cross-model-review.md +191 -0
  57. package/skills/ce-code-review/references/depth-paths.md +32 -0
  58. package/skills/ce-code-review/references/diff-scope.md +59 -0
  59. package/skills/ce-code-review/references/dispatch-reviewers.md +135 -0
  60. package/skills/ce-code-review/references/findings-schema.json +125 -0
  61. package/skills/ce-code-review/references/finish-input.md +118 -0
  62. package/skills/ce-code-review/references/finish-review.md +202 -0
  63. package/skills/ce-code-review/references/intent-and-plan.md +73 -0
  64. package/skills/ce-code-review/references/modes-and-output.md +156 -0
  65. package/skills/ce-code-review/references/persona-catalog.md +66 -0
  66. package/skills/ce-code-review/references/personas/adversarial-reviewer.md +111 -0
  67. package/skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
  68. package/skills/ce-code-review/references/personas/api-contract-reviewer.md +44 -0
  69. package/skills/ce-code-review/references/personas/correctness-reviewer.md +48 -0
  70. package/skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
  71. package/skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
  72. package/skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +45 -0
  73. package/skills/ce-code-review/references/personas/learnings-researcher.md +260 -0
  74. package/skills/ce-code-review/references/personas/maintainability-reviewer.md +85 -0
  75. package/skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
  76. package/skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
  77. package/skills/ce-code-review/references/personas/project-standards-reviewer.md +77 -0
  78. package/skills/ce-code-review/references/personas/reliability-reviewer.md +49 -0
  79. package/skills/ce-code-review/references/personas/security-reviewer.md +50 -0
  80. package/skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
  81. package/skills/ce-code-review/references/personas/testing-reviewer.md +51 -0
  82. package/skills/ce-code-review/references/review-output-template.md +172 -0
  83. package/skills/ce-code-review/references/scope.md +176 -0
  84. package/skills/ce-code-review/references/select-and-route.md +93 -0
  85. package/skills/ce-code-review/references/subagent-template.md +206 -0
  86. package/skills/ce-code-review/references/validator-batch-template.md +67 -0
  87. package/skills/ce-code-review/scripts/cross-model-adversarial-review.sh +1275 -0
  88. package/skills/ce-code-review/scripts/findings-mechanics.py +274 -0
  89. package/skills/ce-code-review/scripts/packs-resolve.py +737 -0
  90. package/skills/ce-code-review/scripts/peer-job-runner.py +2594 -0
  91. package/skills/ce-code-review/scripts/review-scope.py +419 -0
  92. package/skills/ce-code-review/scripts/run-log.py +280 -0
  93. package/skills/ce-commit/SKILL.md +60 -0
  94. package/skills/ce-commit-push-pr/SKILL.md +69 -0
  95. package/skills/ce-commit-push-pr/references/apply-and-handoff.md +79 -0
  96. package/skills/ce-commit-push-pr/references/branch-creation.md +47 -0
  97. package/skills/ce-commit-push-pr/references/commit-and-push.md +26 -0
  98. package/skills/ce-commit-push-pr/references/compose.md +29 -0
  99. package/skills/ce-commit-push-pr/references/context.md +42 -0
  100. package/skills/ce-commit-push-pr/references/gh-stack-cli.md +83 -0
  101. package/skills/ce-commit-push-pr/references/pr-description-writing.md +208 -0
  102. package/skills/ce-commit-push-pr/references/stack-submit.md +81 -0
  103. package/skills/ce-compound/SKILL.md +79 -0
  104. package/skills/ce-compound/assets/resolution-template.md +96 -0
  105. package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
  106. package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
  107. package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
  108. package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
  109. package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
  110. package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
  111. package/skills/ce-compound/references/agents/session-historian.md +82 -0
  112. package/skills/ce-compound/references/assembly.md +127 -0
  113. package/skills/ce-compound/references/concepts-vocabulary.md +98 -0
  114. package/skills/ce-compound/references/enhancement.md +42 -0
  115. package/skills/ce-compound/references/grounding-validation.md +89 -0
  116. package/skills/ce-compound/references/lightweight.md +56 -0
  117. package/skills/ce-compound/references/modes.md +13 -0
  118. package/skills/ce-compound/references/refresh-and-discoverability.md +96 -0
  119. package/skills/ce-compound/references/report.md +115 -0
  120. package/skills/ce-compound/references/research.md +144 -0
  121. package/skills/ce-compound/references/schema.yaml +236 -0
  122. package/skills/ce-compound/references/session-history.md +73 -0
  123. package/skills/ce-compound/references/yaml-schema.md +130 -0
  124. package/skills/ce-compound/scripts/packs-resolve.py +737 -0
  125. package/skills/ce-compound/scripts/session-history/__pycache__/dsh_session.cpython-314.pyc +0 -0
  126. package/skills/ce-compound/scripts/session-history/discover-sessions.sh +290 -0
  127. package/skills/ce-compound/scripts/session-history/discover_dsh.py +123 -0
  128. package/skills/ce-compound/scripts/session-history/dsh_session.py +225 -0
  129. package/skills/ce-compound/scripts/session-history/extract-errors.py +178 -0
  130. package/skills/ce-compound/scripts/session-history/extract-metadata.py +301 -0
  131. package/skills/ce-compound/scripts/session-history/extract-skeleton.py +346 -0
  132. package/skills/ce-compound/scripts/validate-doc-claims.py +497 -0
  133. package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
  134. package/skills/ce-compound-refresh/SKILL.md +88 -0
  135. package/skills/ce-compound-refresh/assets/resolution-template.md +96 -0
  136. package/skills/ce-compound-refresh/references/classify.md +42 -0
  137. package/skills/ce-compound-refresh/references/commit.md +7 -0
  138. package/skills/ce-compound-refresh/references/concepts-vocabulary.md +116 -0
  139. package/skills/ce-compound-refresh/references/discoverability.md +15 -0
  140. package/skills/ce-compound-refresh/references/investigate.md +23 -0
  141. package/skills/ce-compound-refresh/references/modes.md +27 -0
  142. package/skills/ce-compound-refresh/references/per-action-flows.md +142 -0
  143. package/skills/ce-compound-refresh/references/report.md +23 -0
  144. package/skills/ce-compound-refresh/references/schema.yaml +236 -0
  145. package/skills/ce-compound-refresh/references/scope.md +18 -0
  146. package/skills/ce-compound-refresh/references/worth-audit.md +35 -0
  147. package/skills/ce-compound-refresh/references/yaml-schema.md +130 -0
  148. package/skills/ce-compound-refresh/scripts/validate-doc-claims.py +497 -0
  149. package/skills/ce-compound-refresh/scripts/validate-frontmatter.py +137 -0
  150. package/skills/ce-debug/SKILL.md +122 -0
  151. package/skills/ce-debug/references/anti-patterns.md +91 -0
  152. package/skills/ce-debug/references/defense-in-depth.md +39 -0
  153. package/skills/ce-debug/references/fix.md +21 -0
  154. package/skills/ce-debug/references/investigate.md +114 -0
  155. package/skills/ce-debug/references/investigation-techniques.md +384 -0
  156. package/skills/ce-debug/references/pipeline-mode.md +78 -0
  157. package/skills/ce-debug/references/post-fix-handoff.md +60 -0
  158. package/skills/ce-debug/references/return-to-caller.md +57 -0
  159. package/skills/ce-doc-review/SKILL.md +75 -0
  160. package/skills/ce-doc-review/references/bulk-preview.md +146 -0
  161. package/skills/ce-doc-review/references/cross-model-review.md +207 -0
  162. package/skills/ce-doc-review/references/decision-primer.md +47 -0
  163. package/skills/ce-doc-review/references/dispatch.md +56 -0
  164. package/skills/ce-doc-review/references/document-intake.md +44 -0
  165. package/skills/ce-doc-review/references/findings-schema.json +85 -0
  166. package/skills/ce-doc-review/references/modes.md +20 -0
  167. package/skills/ce-doc-review/references/open-questions-defer.md +158 -0
  168. package/skills/ce-doc-review/references/persona-selection.md +41 -0
  169. package/skills/ce-doc-review/references/personas/adversarial-document-reviewer.md +109 -0
  170. package/skills/ce-doc-review/references/personas/coherence-reviewer.md +69 -0
  171. package/skills/ce-doc-review/references/personas/design-lens-reviewer.md +48 -0
  172. package/skills/ce-doc-review/references/personas/feasibility-reviewer.md +50 -0
  173. package/skills/ce-doc-review/references/personas/product-lens-reviewer.md +85 -0
  174. package/skills/ce-doc-review/references/personas/scope-guardian-reviewer.md +83 -0
  175. package/skills/ce-doc-review/references/personas/security-lens-reviewer.md +41 -0
  176. package/skills/ce-doc-review/references/personas/whole-doc-reviewer.md +25 -0
  177. package/skills/ce-doc-review/references/rendering-floor.md +175 -0
  178. package/skills/ce-doc-review/references/review-output-template.md +123 -0
  179. package/skills/ce-doc-review/references/subagent-template.md +188 -0
  180. package/skills/ce-doc-review/references/synthesis-and-presentation.md +350 -0
  181. package/skills/ce-doc-review/references/walkthrough.md +347 -0
  182. package/skills/ce-doc-review/scripts/cross-model-doc-review.sh +1270 -0
  183. package/skills/ce-doc-review/scripts/packs-resolve.py +737 -0
  184. package/skills/ce-doc-review/scripts/peer-job-runner.py +2594 -0
  185. package/skills/ce-dogfood/SKILL.md +66 -0
  186. package/skills/ce-dogfood/agents/openai.yaml +2 -0
  187. package/skills/ce-dogfood/references/dogfood-report-template.md +97 -0
  188. package/skills/ce-dogfood/references/phases.md +167 -0
  189. package/skills/ce-dogfood/references/test-matrix-taxonomy.md +62 -0
  190. package/skills/ce-dogfood/scripts/packs-resolve.py +737 -0
  191. package/skills/ce-explain/SKILL.md +81 -0
  192. package/skills/ce-explain/references/agents/behavior-trace-scout.md +19 -0
  193. package/skills/ce-explain/references/agents/work-recap-scout.md +23 -0
  194. package/skills/ce-explain/references/check-in.md +18 -0
  195. package/skills/ce-explain/references/destinations.md +35 -0
  196. package/skills/ce-explain/references/explainer-html.md +29 -0
  197. package/skills/ce-explain/references/explainer-markdown.md +24 -0
  198. package/skills/ce-explain/references/intake.md +50 -0
  199. package/skills/ce-explain/references/orchestration.md +46 -0
  200. package/skills/ce-handoff/SKILL.md +54 -0
  201. package/skills/ce-handoff/references/create.md +88 -0
  202. package/skills/ce-handoff/references/resume.md +46 -0
  203. package/skills/ce-ideate/SKILL.md +75 -0
  204. package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +166 -0
  205. package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
  206. package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
  207. package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
  208. package/skills/ce-ideate/references/decomposition.md +39 -0
  209. package/skills/ce-ideate/references/divergent-ideation.md +112 -0
  210. package/skills/ce-ideate/references/grounding.md +138 -0
  211. package/skills/ce-ideate/references/html-rendering.md +638 -0
  212. package/skills/ce-ideate/references/ideation-sections.md +191 -0
  213. package/skills/ce-ideate/references/issue-intelligence.md +44 -0
  214. package/skills/ce-ideate/references/markdown-rendering.md +242 -0
  215. package/skills/ce-ideate/references/output-mode.md +34 -0
  216. package/skills/ce-ideate/references/post-ideation-workflow.md +169 -0
  217. package/skills/ce-ideate/references/scope-gates.md +148 -0
  218. package/skills/ce-ideate/references/universal-ideation.md +119 -0
  219. package/skills/ce-ideate/references/user-research-artifacts.md +28 -0
  220. package/skills/ce-ideate/references/web-research-cache.md +56 -0
  221. package/skills/ce-noslop/SKILL.md +45 -0
  222. package/skills/ce-noslop/references/patterns.md +61 -0
  223. package/skills/ce-noslop/references/terminology.md +3 -0
  224. package/skills/ce-optimize/SKILL.md +64 -0
  225. package/skills/ce-optimize/references/agents/learnings-researcher.md +247 -0
  226. package/skills/ce-optimize/references/agents/repo-research-analyst.md +258 -0
  227. package/skills/ce-optimize/references/example-expensive-benchmark-spec.yaml +88 -0
  228. package/skills/ce-optimize/references/example-hard-spec.yaml +64 -0
  229. package/skills/ce-optimize/references/example-judge-spec.yaml +83 -0
  230. package/skills/ce-optimize/references/example-text-target-spec.yaml +80 -0
  231. package/skills/ce-optimize/references/experiment-log-schema.yaml +563 -0
  232. package/skills/ce-optimize/references/experiment-prompt-template.md +143 -0
  233. package/skills/ce-optimize/references/judge-prompt-template.md +117 -0
  234. package/skills/ce-optimize/references/loop.md +286 -0
  235. package/skills/ce-optimize/references/measurement.md +168 -0
  236. package/skills/ce-optimize/references/optimize-spec-schema.yaml +570 -0
  237. package/skills/ce-optimize/references/persistence.md +93 -0
  238. package/skills/ce-optimize/references/spec.md +107 -0
  239. package/skills/ce-optimize/references/text-targets.md +51 -0
  240. package/skills/ce-optimize/references/usage-guide.md +147 -0
  241. package/skills/ce-optimize/references/wrap-up.md +54 -0
  242. package/skills/ce-optimize/scripts/decide.mjs +676 -0
  243. package/skills/ce-optimize/scripts/experiment-worktree.sh +330 -0
  244. package/skills/ce-optimize/scripts/measure.sh +140 -0
  245. package/skills/ce-optimize/scripts/parallel-probe.sh +134 -0
  246. package/skills/ce-plan/SKILL.md +60 -0
  247. package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +66 -0
  248. package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
  249. package/skills/ce-plan/references/agents/best-practices-researcher.md +118 -0
  250. package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
  251. package/skills/ce-plan/references/agents/framework-docs-researcher.md +97 -0
  252. package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
  253. package/skills/ce-plan/references/agents/learnings-researcher.md +268 -0
  254. package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
  255. package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
  256. package/skills/ce-plan/references/agents/repo-research-analyst.md +262 -0
  257. package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
  258. package/skills/ce-plan/references/agents/slack-researcher.md +131 -0
  259. package/skills/ce-plan/references/agents/spec-flow-analyzer.md +84 -0
  260. package/skills/ce-plan/references/agents/web-researcher.md +125 -0
  261. package/skills/ce-plan/references/approach-altitude.md +55 -0
  262. package/skills/ce-plan/references/bakeoff.md +9 -0
  263. package/skills/ce-plan/references/deepening-workflow.md +263 -0
  264. package/skills/ce-plan/references/final-review.md +134 -0
  265. package/skills/ce-plan/references/html-rendering.md +638 -0
  266. package/skills/ce-plan/references/intake.md +158 -0
  267. package/skills/ce-plan/references/markdown-rendering.md +242 -0
  268. package/skills/ce-plan/references/output-contracts.md +29 -0
  269. package/skills/ce-plan/references/output-mode.md +45 -0
  270. package/skills/ce-plan/references/plan-handoff.md +116 -0
  271. package/skills/ce-plan/references/plan-sections.md +489 -0
  272. package/skills/ce-plan/references/reasoning-elevation.md +124 -0
  273. package/skills/ce-plan/references/research.md +211 -0
  274. package/skills/ce-plan/references/resume.md +61 -0
  275. package/skills/ce-plan/references/settled-decisions.md +45 -0
  276. package/skills/ce-plan/references/structure.md +207 -0
  277. package/skills/ce-plan/references/synthesis-summary.md +260 -0
  278. package/skills/ce-plan/references/universal-planning.md +170 -0
  279. package/skills/ce-plan/scripts/elevation-dispatch.sh +288 -0
  280. package/skills/ce-plan/scripts/packs-resolve.py +737 -0
  281. package/skills/ce-plan/scripts/peer-job-runner.py +2594 -0
  282. package/skills/ce-polish/SKILL.md +22 -0
  283. package/skills/ce-polish/agents/openai.yaml +2 -0
  284. package/skills/ce-polish/references/dev-server-astro.md +58 -0
  285. package/skills/ce-polish/references/dev-server-detection.md +40 -0
  286. package/skills/ce-polish/references/dev-server-next.md +61 -0
  287. package/skills/ce-polish/references/dev-server-nuxt.md +58 -0
  288. package/skills/ce-polish/references/dev-server-procfile.md +59 -0
  289. package/skills/ce-polish/references/dev-server-rails.md +50 -0
  290. package/skills/ce-polish/references/dev-server-remix.md +58 -0
  291. package/skills/ce-polish/references/dev-server-sveltekit.md +58 -0
  292. package/skills/ce-polish/references/dev-server-vite.md +48 -0
  293. package/skills/ce-polish/references/install-riffrec.md +35 -0
  294. package/skills/ce-polish/references/launch-json-schema.md +177 -0
  295. package/skills/ce-polish/references/live-loop.md +98 -0
  296. package/skills/ce-polish/references/live-remote.md +35 -0
  297. package/skills/ce-polish/references/live-start.md +93 -0
  298. package/skills/ce-polish/references/live-stream-contract.md +253 -0
  299. package/skills/ce-polish/references/run.md +67 -0
  300. package/skills/ce-polish/scripts/detect-project-type.sh +277 -0
  301. package/skills/ce-polish/scripts/detect-riffrec.sh +225 -0
  302. package/skills/ce-polish/scripts/live-endpoint.js +2841 -0
  303. package/skills/ce-polish/scripts/read-launch-json.sh +117 -0
  304. package/skills/ce-polish/scripts/resolve-package-manager.sh +95 -0
  305. package/skills/ce-polish/scripts/resolve-port.sh +307 -0
  306. package/skills/ce-pov/SKILL.md +64 -0
  307. package/skills/ce-pov/references/agents/external-evidence-researcher.md +26 -0
  308. package/skills/ce-pov/references/agents/pov-peer.md +43 -0
  309. package/skills/ce-pov/references/agents/precedent-activity-scout.md +21 -0
  310. package/skills/ce-pov/references/agents/project-grounding-scout.md +32 -0
  311. package/skills/ce-pov/references/boundaries.md +36 -0
  312. package/skills/ce-pov/references/cross-model-panel.md +596 -0
  313. package/skills/ce-pov/references/followup.md +9 -0
  314. package/skills/ce-pov/references/grounding.md +47 -0
  315. package/skills/ce-pov/references/intake.md +61 -0
  316. package/skills/ce-pov/references/invocation.md +47 -0
  317. package/skills/ce-pov/references/method.md +68 -0
  318. package/skills/ce-pov/references/pov-schema.json +56 -0
  319. package/skills/ce-pov/references/report.md +7 -0
  320. package/skills/ce-pov/scripts/cross-model-pov.sh +1110 -0
  321. package/skills/ce-pov/scripts/peer-job-runner.py +2594 -0
  322. package/skills/ce-product-pulse/SKILL.md +81 -0
  323. package/skills/ce-product-pulse/agents/openai.yaml +2 -0
  324. package/skills/ce-product-pulse/references/config.md +20 -0
  325. package/skills/ce-product-pulse/references/interview.md +260 -0
  326. package/skills/ce-product-pulse/references/report-template.md +89 -0
  327. package/skills/ce-product-pulse/references/run.md +46 -0
  328. package/skills/ce-product-pulse/references/setup.md +43 -0
  329. package/skills/ce-promote/SKILL.md +90 -0
  330. package/skills/ce-promote/agents/openai.yaml +2 -0
  331. package/skills/ce-promote/references/spiral-cli.md +149 -0
  332. package/skills/ce-proof/SKILL.md +65 -0
  333. package/skills/ce-proof/references/api.md +176 -0
  334. package/skills/ce-proof/references/workflows.md +124 -0
  335. package/skills/ce-prototype/SKILL.md +59 -0
  336. package/skills/ce-prototype/assets/annotate.css +206 -0
  337. package/skills/ce-prototype/assets/annotate.js +837 -0
  338. package/skills/ce-prototype/references/annotation-loop.md +32 -0
  339. package/skills/ce-prototype/references/build.md +39 -0
  340. package/skills/ce-prototype/references/craft-floor.md +43 -0
  341. package/skills/ce-prototype/references/preview.md +118 -0
  342. package/skills/ce-prototype/references/scoping.md +38 -0
  343. package/skills/ce-prototype/references/write-back.md +37 -0
  344. package/skills/ce-prototype/scripts/light-webserver.js +1275 -0
  345. package/skills/ce-resolve-pr-feedback/SKILL.md +60 -0
  346. package/skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +59 -0
  347. package/skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +141 -0
  348. package/skills/ce-resolve-pr-feedback/references/full-mode.md +336 -0
  349. package/skills/ce-resolve-pr-feedback/references/pipeline-mode.md +23 -0
  350. package/skills/ce-resolve-pr-feedback/references/resume.md +60 -0
  351. package/skills/ce-resolve-pr-feedback/references/return-to-caller.md +111 -0
  352. package/skills/ce-resolve-pr-feedback/references/targeted-mode.md +48 -0
  353. package/skills/ce-resolve-pr-feedback/scripts/get-pr-comments +176 -0
  354. package/skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +84 -0
  355. package/skills/ce-resolve-pr-feedback/scripts/pending-feedback.py +287 -0
  356. package/skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +57 -0
  357. package/skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
  358. package/skills/ce-retune/SKILL.md +42 -0
  359. package/skills/ce-retune/agents/openai.yaml +2 -0
  360. package/skills/ce-retune/references/baseline-mining.md +122 -0
  361. package/skills/ce-retune/references/corpus-audit.md +128 -0
  362. package/skills/ce-retune/references/cut-passes.md +129 -0
  363. package/skills/ce-retune/references/halt-taxonomy.md +123 -0
  364. package/skills/ce-retune/references/noise-floor.md +131 -0
  365. package/skills/ce-retune/references/workflow-shapes.md +73 -0
  366. package/skills/ce-riffrec-feedback-analysis/SKILL.md +33 -0
  367. package/skills/ce-riffrec-feedback-analysis/references/analyzer.md +18 -0
  368. package/skills/ce-riffrec-feedback-analysis/references/compound-engineering-feedback-format.md +116 -0
  369. package/skills/ce-riffrec-feedback-analysis/references/extensive-analysis.md +98 -0
  370. package/skills/ce-riffrec-feedback-analysis/references/install-riffrec.md +27 -0
  371. package/skills/ce-riffrec-feedback-analysis/references/quick-bug-report.md +38 -0
  372. package/skills/ce-riffrec-feedback-analysis/scripts/analyze_riffrec_zip.py +1306 -0
  373. package/skills/ce-setup/SKILL.md +103 -0
  374. package/skills/ce-setup/agents/openai.yaml +2 -0
  375. package/skills/ce-setup/assets/compounding-directive.md +11 -0
  376. package/skills/ce-setup/assets/noslop-directive.md +7 -0
  377. package/skills/ce-setup/assets/pack-rule-template.md +11 -0
  378. package/skills/ce-setup/references/acpx-cache-warm.md +14 -0
  379. package/skills/ce-setup/references/config-template.yaml +210 -0
  380. package/skills/ce-setup/references/legacy-codex-tool-map.md +15 -0
  381. package/skills/ce-setup/references/pack-scaffold.md +45 -0
  382. package/skills/ce-setup/references/repo-fixes.md +121 -0
  383. package/skills/ce-setup/scripts/check-health +929 -0
  384. package/skills/ce-setup/scripts/packs-resolve.py +737 -0
  385. package/skills/ce-simplify-code/SKILL.md +76 -0
  386. package/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
  387. package/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
  388. package/skills/ce-simplify-code/references/personas/comment-reviewer.md +24 -0
  389. package/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
  390. package/skills/ce-simplify-code/references/personas/test-value-reviewer.md +23 -0
  391. package/skills/ce-simplify-code/references/reviewer-dispatch.md +11 -0
  392. package/skills/ce-simplify-code/references/standalone-report.md +25 -0
  393. package/skills/ce-strategy/SKILL.md +61 -0
  394. package/skills/ce-strategy/references/grounding.md +22 -0
  395. package/skills/ce-strategy/references/interview.md +179 -0
  396. package/skills/ce-strategy/references/strategy-template.md +94 -0
  397. package/skills/ce-strategy/references/update-run.md +33 -0
  398. package/skills/ce-sweep/SKILL.md +93 -0
  399. package/skills/ce-sweep/agents/openai.yaml +2 -0
  400. package/skills/ce-sweep/references/agents/media-analyzer.md +54 -0
  401. package/skills/ce-sweep/references/interview.md +208 -0
  402. package/skills/ce-sweep/references/model-tiers.md +13 -0
  403. package/skills/ce-sweep/references/plan-template.md +62 -0
  404. package/skills/ce-sweep/references/run.md +126 -0
  405. package/skills/ce-sweep/references/sources/email.md +57 -0
  406. package/skills/ce-sweep/references/sources/github-issues.md +59 -0
  407. package/skills/ce-sweep/references/sources/slack.md +61 -0
  408. package/skills/ce-sweep/references/state-schema.md +157 -0
  409. package/skills/ce-sweep/references/subagent-template.md +60 -0
  410. package/skills/ce-sweep/scripts/analyze_riffrec_zip.py +1306 -0
  411. package/skills/ce-sweep/scripts/sweep-state.py +793 -0
  412. package/skills/ce-test-browser/SKILL.md +50 -0
  413. package/skills/ce-test-browser/references/agent-browser-driver.md +47 -0
  414. package/skills/ce-test-browser/references/pipeline-orchestration.md +45 -0
  415. package/skills/ce-test-browser/references/route-and-report.md +129 -0
  416. package/skills/ce-test-browser/scripts/resolve-port.sh +41 -0
  417. package/skills/ce-test-xcode/SKILL.md +26 -0
  418. package/skills/ce-test-xcode/agents/openai.yaml +2 -0
  419. package/skills/ce-test-xcode/references/setup-and-build.md +32 -0
  420. package/skills/ce-test-xcode/references/test-and-report.md +64 -0
  421. package/skills/ce-work/SKILL.md +62 -0
  422. package/skills/ce-work/references/agents/figma-design-sync.md +27 -0
  423. package/skills/ce-work/references/agents/implementation-worker.md +17 -0
  424. package/skills/ce-work/references/cross-model-execution.md +191 -0
  425. package/skills/ce-work/references/execution-engines.md +64 -0
  426. package/skills/ce-work/references/execution-strategy.md +73 -0
  427. package/skills/ce-work/references/implementation-loop.md +107 -0
  428. package/skills/ce-work/references/implementation-result-schema.json +52 -0
  429. package/skills/ce-work/references/input-triage.md +50 -0
  430. package/skills/ce-work/references/non-code-execution.md +23 -0
  431. package/skills/ce-work/references/return-to-caller.md +33 -0
  432. package/skills/ce-work/references/review-findings-followup.md +82 -0
  433. package/skills/ce-work/references/shipping-workflow.md +89 -0
  434. package/skills/ce-work/references/tracker-defer.md +150 -0
  435. package/skills/ce-work/references/work-intake.md +51 -0
  436. package/skills/ce-work/references/workspace-setup.md +27 -0
  437. package/skills/ce-work/scripts/__pycache__/unit_workspace_jobs.cpython-314.pyc +0 -0
  438. package/skills/ce-work/scripts/__pycache__/unit_workspace_state.cpython-314.pyc +0 -0
  439. package/skills/ce-work/scripts/cross-model-work.sh +1385 -0
  440. package/skills/ce-work/scripts/peer-job-runner.py +2594 -0
  441. package/skills/ce-work/scripts/unit-workspace.py +215 -0
  442. package/skills/ce-work/scripts/unit_workspace_ignored.py +93 -0
  443. package/skills/ce-work/scripts/unit_workspace_integration.py +677 -0
  444. package/skills/ce-work/scripts/unit_workspace_jobs.py +1216 -0
  445. package/skills/ce-work/scripts/unit_workspace_lifecycle.py +981 -0
  446. package/skills/ce-work/scripts/unit_workspace_state.py +1379 -0
  447. package/skills/ce-work/scripts/unit_workspace_transaction.py +612 -0
  448. package/skills/ce-worktree/SKILL.md +54 -0
  449. package/skills/lfg/SKILL.md +59 -0
  450. package/skills/lfg/references/debug-return.md +25 -0
  451. package/skills/lfg/references/intake.md +30 -0
  452. package/skills/lfg/references/next-work-handoff.md +96 -0
  453. package/skills/lfg/references/plan-brief.md +36 -0
  454. package/skills/lfg/references/review-followup.md +78 -0
  455. package/skills/lfg/references/shipping.md +65 -0
  456. package/skills/lfg/references/stage-routing.md +49 -0
  457. package/skills/lfg/references/task-visibility.md +11 -0
  458. package/skills/lfg/references/tracker-defer.md +150 -0
  459. package/skills/lfg/references/work-return.md +27 -0
  460. package/skills/wtf/SKILL.md +33 -0
  461. package/skills/wtf/agents/openai.yaml +2 -0
@@ -0,0 +1,253 @@
1
+ # Live stream contract (`live/1`)
2
+
3
+ The wire contract between a riffrec live page, the `scripts/live-endpoint.js` helper, and the coding agent. This is the skill's own copy of the contract; the page's copy ships with the riffrec package and the two are kept identical by that package's fixtures. When they disagree, the endpoint rejects the page's `schema_version` and the page shows its incompatible-endpoint state instead of parsing best-effort.
4
+
5
+ Mirrors riffrec `docs/live-stream-contract.md` @ 55be284 (PR #29; `src/live/contract.ts`, `src/live/tools.ts`, and `src/live/realtime/persona.ts` are the typed source of truth). Adding an optional payload field or a new event type is not a breaking change; consumers ignore fields they do not know but reject event types they do not know.
6
+
7
+ ## Envelope
8
+
9
+ Every page -> endpoint message is one envelope:
10
+
11
+ ```json
12
+ { "schema_version": "live/1", "session_id": "<page-minted id>", "seq": 12, "t": 1726000000000, "type": "unit", "payload": { } }
13
+ ```
14
+
15
+ - `seq` is a per-session monotonic integer starting at 1. The endpoint deduplicates on `(session_id, seq)`, applies envelopes strictly in `seq` order (one that arrives ahead of a gap waits, unacknowledged, until the gap closes), and acknowledges the highest contiguous `seq`; after an outage the page replays from the last acknowledged `seq`.
16
+ - `type` is one of the four riffrec capture events (`click`, `navigation`, `network_request`, `console_error`) or `transcript`, `unit`, `unit_update`, `unit_withdraw`, `annotation`, `checkpoint`, `answer`, `frame`, `mic`, `mode`, `stream_state`.
17
+ - `frame` envelopes are posted alone, never in a batch with other events.
18
+ - An unsupported `schema_version` is answered `409 { "expected_schema_version": "live/1" }`; any other invalid envelope is `400 { "reason": <not_object | missing_session_id | session_mismatch | missing_seq | invalid_seq | invalid_t | unknown_type | invalid_payload>, "seq" }`. A storage failure while applying answers `500 { "error": "storage_failed", "acked_seq" }`; each envelope is applied, stored, and saved as one transaction, so a failure rolls that envelope back (no wake or SSE notice escapes before the commit), everything acknowledged before it stays acknowledged, and the rest is retriable. Payloads are checked against the shapes above (the same rules as riffrec's `validateEnvelope`) before anything in the body is acknowledged; a rejected body acknowledges nothing.
19
+
20
+ ## Payload shapes
21
+
22
+ | type | payload |
23
+ |---|---|
24
+ | `unit` | `{ id, statement, transcript_excerpt, anchors[], evidence { frame_ids[], annotation_ids[], transcript_span, telemetry_window?, audio_clip_id? }, status, confirmed? }`; status follows `initial` → `triaging` → `accepted` or `needs_info`, then `applied` or `blocked`, and `withdrawn` when the riffer retracts it; `confirmed { element, change }` is present after the final confirmation pass |
25
+ | `anchor` (inside units and annotations) | `{ route, selector, component?, rect, t }` |
26
+ | `annotation` | `{ id, kind: "stroke" \| "pin", points[], bbox, anchor, text?, unit_id?, composite_frame_id? }` |
27
+ | `transcript` | `{ id, role: "riffer" \| "interviewer", text, t_start, t_end, final }` |
28
+ | `unit_update` | `{ unit_id, statement?, anchors_add?, confirmed? }`; `statement`/`anchors_add` apply only while the unit is still `initial` and unreleased (KTD5), `confirmed` at any time |
29
+ | `unit_withdraw` | `{ unit_id, reason? }` |
30
+ | `checkpoint` | `{ id, trigger, mode }`. riffrec's `CheckpointTrigger` is `silence \| page_change \| send \| answer \| mode_change \| final`; the page sends only `silence`, `page_change`, `send`, `final` (the overlay's Done control, after the confirmation pass and before `/session/end`), and this endpoint answers `400 invalid_payload` to `answer`/`mode_change` on the wire since it produces those two itself |
31
+ | `answer` | `{ unit_id, text }` |
32
+ | `frame` | `{ id, t, route, kind: "gesture" \| "periodic" \| "composite", jpeg_base64, dropped?: "quota" \| "oversize" }`. `dropped` means the page discarded the bytes but kept the frame's `seq` so numbering stays contiguous (`quota`: the buffering queue evicted it; `oversize`: after a `413`); `jpeg_base64` is then empty, the endpoint keeps id and metadata, writes no image, and treats the frame as absent |
33
+ | `mic` | `{ state: "granted" \| "denied" \| "muted" \| "unmuted" }` |
34
+ | `mode` | `{ mode: "instant" \| "smart" \| "collect" }` (default `smart`; the switch takes effect at the next checkpoint) |
35
+ | `stream_state` | `{ state: "streaming" \| "buffering" \| "unloading" }` (`unloading` is sent on `pagehide` with `keepalive` so an ordinary reload classifies without waiting for the page-lost grace window) |
36
+
37
+ ## Credentials
38
+
39
+ `start` mints two tokens and writes both to `state/session.json` (mode 0600, inside a 0700 `state/`). Only the page token is printed. Every route reads its credential from `Authorization: Bearer <token>` and nothing else: no query string, no cookie, no header alias.
40
+
41
+ | Class | Token | Extra requirement | Wrong class |
42
+ |---|---|---|---|
43
+ | Page routes | page token | `X-Riffrec-Session: <session_id>`; an `Origin` header, when present, must equal `--app-origin` | agent token -> 403 |
44
+ | Agent routes | agent token | no `Origin` header at all (any `Origin` -> 403); no CORS headers are emitted | page token -> 403 |
45
+
46
+ A missing or unknown token is 401. The endpoint binds the page token to the first `session_id` it sees; any other id is answered `409 { "active_session_id" }`. Once the session ends, the ended id receives `410 { "status": "session-ended" }`. A different id opens a fresh board on the same page token and is handled as that session's first request, or gets `409 { "error": "previous_session_draining" }` while the ended session still holds batches or its upload is in flight.
47
+
48
+ Page routes answer `OPTIONS` with `Access-Control-Allow-Origin: <exact --app-origin>`, `Access-Control-Allow-Headers: Authorization, Content-Type, X-Riffrec-Session, X-Riffrec-OpenAI-Key`, `Access-Control-Allow-Methods: GET, POST`, `Vary: Origin`, and no credentials flag.
49
+
50
+ ## HTTP surface
51
+
52
+ ### Page routes
53
+
54
+ | Route | Body | Response |
55
+ |---|---|---|
56
+ | `POST /events` | one envelope or an array of envelopes | `200 { "acked_seq" }`. Body cap 64 KB, or 2 MB for a lone `frame`; oversize is `413 { "max_bytes": 65536, "frame_max_bytes": 2097152 }` and does not count toward the page's buffering threshold. Beyond the 500 MB per-session disk cap (`CE_LIVE_DISK_CAP_BYTES`), frames that carry image bytes are refused with `507 { "reason": "disk_cap", "stream_state": "buffering", "max_bytes", "acked_seq" }`; a `dropped` frame and every other envelope still flow. Past a hard ceiling above that (`CE_LIVE_DISK_HARD_CAP_BYTES`, default the cap plus 64 MB), every envelope is refused the same way, so a session cannot grow the log without bound; the page treats the refusal as `buffering` (three failures) and replays once accepted again. |
57
+ | `GET /stream` | none | SSE. Event names: `ack { acked_seq }`, `unit_status { unit_id, status, note?, guess? }`, `applied { checkpoint_id, unit_ids[] }`, `ask { unit_id, question }`, `session_ended { reason, session_id, log_dir }`, `agent { state, since, checkpoint_id? }` (`listening` while a `wait` is parked, `working` from the moment a batch is served until the next `wait`, `away` once no `wait` has parked for 15 s). On connect the stream replays `ack`, the current `agent` state, a `unit_status` (with `note`/`guess`) for every released or withdrawn unit, and an `ask` for every unit still in `needs_info`, so a reloaded or reconnected page reconciles its board. **The response is ended 300 ms (`CE_LIVE_STREAM_FLUSH_MS`) after any delivery other than a bare `ack`**: TLS-terminating intermediaries that hold a streaming body until it completes (cloudflared quick tunnels, which sit behind a Worker) then release it at once, and riffrec's client reopens the stream a second later and reconciles from the replay. On a direct path the same frames arrive as written. |
58
+ | `POST /mint` | `{ "session_id" }` | `200 { "client_secret", "expires_at", "model" }`; `410` if the session ended while the upstream call was in flight; `403 { "reason": "tls_required" }` when the peer is not loopback, unless the peer is an address named with `--trust-proxy` and the request carries `X-Forwarded-Proto: https` (the header alone is never trusted); `429 { "retry_after" }` past one mint in flight or five per minute; `502 { "reason": "openai_error", "upstream_status" }` with the upstream body discarded; `503 { "reason": "no_key" \| "brief_contains_secret" }`. |
59
+ | `POST /session/end` | the page's full-evidence archive (`application/zip` or `application/json`; may be empty) | `200 { "status": "session-ended", "log_dir", "archive_bytes" }`; `409 { "error": "session_end_in_progress" }` while another upload is in flight; `500 { "error": "archive_write_failed" }` if the disk refuses the archive or the terminal state cannot be persisted (the session stays live and the call can be retried). Stores the archive under `state/log/`, emits a `final` checkpoint only if the page never sent one and something is still held or accepted, and closes every stream with `session_ended`. The page token stays valid. |
60
+ | `GET /session` | none | `200 { "status": "live" \| "ended", "session_id": <string \| null>, "accepts_new_session": <boolean> }`; `accepts_new_session` is true once the board has ended and nothing is held or draining. Page token only: no `X-Riffrec-Session`, binds nothing, and does not count as activity for the idle timeout. |
61
+
62
+ ### Agent routes
63
+
64
+ | Route | Body | Response |
65
+ |---|---|---|
66
+ | `GET /wait` | none | Long-poll. `200 <wake envelope>`; `204` after the poll window (the CLI loops); `409 { "status": "wait-taken" }` when another wait is parked; `410 { "status": "session-ended" }` when the session ended and nothing is held (the agent token itself stays valid until `stop`). |
67
+ | `POST /checkpoints/:id/ack` | `{}` | `200 { "ok", "checkpoint_id" }`; idempotent: a repeated ack of a batch this session already dropped is `200 { "ok", "checkpoint_id", "already_acked": true }`; `404` for a checkpoint the session never served. |
68
+ | `POST /units/:id/status` | `{ "status", "note"?, "guess"? }` with status in `triaging`, `accepted`, `needs_info`, `applied`, `blocked`, `withdrawn` | `200`; relays `unit_status` (and `applied`) on the stream. `accepted` puts the unit in the backlog below; `applied` or `blocked` takes it out. `409` once the page has withdrawn the unit: a withdrawal is terminal. |
69
+ | `POST /units/:id/ask` | `{ "question" }` | `200`; moves the unit to `needs_info` and relays `ask` on the stream. `409` for a withdrawn unit. |
70
+ | `GET /status` | none | The board summary, the same document the `status` CLI prints. |
71
+
72
+ Nothing else is served: there is no file route, and every unknown path is 404.
73
+
74
+ ## Checkpoints and the wake envelope
75
+
76
+ A checkpoint releases every held unit and annotation plus any withdrawal that arrived after an earlier release. On release (checkpoint, instant unit, or mode change) the endpoint marks each unit `triaging` and broadcasts `unit_status: "triaging"` on the stream in the same request, before any agent has seen or acknowledged the batch; the page treats that event as the release marker and the board leaves "Heard". A withdrawal before release removes the unit from the batch; one after release is forwarded in the next batch with `status: "withdrawn"`.
77
+
78
+ - Page-emitted checkpoints: `silence`, `page_change`, `send`, and `final` (the overlay's Done control), each carrying the mode at emission. A `silence`, `page_change`, or `send` checkpoint that releases nothing does not wake the agent; `final` always wakes.
79
+ - Instant mode is continuous pickup: while `mode` is `instant`, every accepted `unit` envelope is released as it lands (together with anything else held) as its own batch, `checkpoint_id: "instant-<unit id>"`, `kind: "instant"`, and wakes the agent at once; a `mode` event switching to `instant` releases what is held as a `mode_change` batch. Page checkpoints under Instant therefore usually release nothing and do not wake. Smart and Collect keep the checkpoint semantics.
80
+ - Endpoint-emitted checkpoints: `answer`, created whenever an `answer` event arrives (carries `answers[]` only and releases no units), and `mode_change`, created the moment a `mode` event leaves Collect (KTD12). Both always wake. riffrec exports the always-wake set as `ALWAYS_WAKE_TRIGGERS = ["answer", "mode_change", "final"]` (KTD9).
81
+ - **Accepted backlog.** Units the endpoint released, the agent posted `accepted` for, and no `applied` or `blocked` has followed. `mode_change` carries the whole backlog in `units[]` (status `accepted`) so a Collect session's work is applied under the new mode; `final` carries the backlog too, after anything newly released. A `mode_change` or `final` envelope may therefore carry units that were already served once, or nothing at all; treat it as work to apply, not a no-op.
82
+ - A page checkpoint id that was already used gets a `-2`, `-3`, … suffix in `checkpoint_id`, so every batch has its own file and ack route.
83
+ - `mode_at_checkpoint` is the mode carried by the releasing checkpoint, or the mode in force for endpoint-emitted checkpoints.
84
+
85
+ `wait` prints one envelope and exits 0:
86
+
87
+ ```json
88
+ {
89
+ "schema_version": "live/1",
90
+ "checkpoint_id": "ck-…",
91
+ "kind": "silence" | "page_change" | "send" | "instant" | "answer" | "mode_change" | "final",
92
+ "mode_at_checkpoint": "instant" | "smart" | "collect",
93
+ "session_status": "live" | "page_lost",
94
+ "units": [ ], "annotations": [ ], "answers": [ ]
95
+ }
96
+ ```
97
+
98
+ Acknowledge with `POST /checkpoints/:id/ack` immediately after parsing. A batch served without an acknowledgment is re-served before any new batch, including after a helper restart: batches persist under `state/batches/` until acknowledged.
99
+
100
+ ### Page-lost
101
+
102
+ After the endpoint relays an `applied` notice, a page stream that closes and does not reconnect within the grace window (default 15 s, `CE_LIVE_PAGE_LOST_GRACE_MS`) marks the episode lost. The next `wait` returns one envelope with `session_status: "page_lost"`, empty `units`, and `lost_after_checkpoint_id`; it is queued like any batch, persisted and re-served until acknowledged; further waits then block until the page reconnects or a new batch exists. A reconnect inside the window is a reload and nothing is reported.
103
+
104
+ ## CLI (`scripts/live-endpoint.js`)
105
+
106
+ | Command | Behavior | Exit |
107
+ |---|---|---|
108
+ | `start --root <dir> [--app-origin <origin>] [--host 127.0.0.1] [--port 0] [--owner-pid <pid>] [--trust-proxy <ip>[,<ip>]] [--foreground]` | Prints `{ url, port, page_token, status }` once; writes `state/session.json`. When `state/session.json` has `ended: false`, this is a resume: the same `page_token` and `agent_token` are reused, the `session_id` binding, board, acknowledged `seq`, and un-acknowledged batches are reloaded, the previous port is preferred, and only `pid`, `owner_pid`, and `url` are rewritten (`status: "resumed"`); the previous `--app-origin`, bind host, and `--trust-proxy` list are kept unless given again, so the documented recovery is a bare `start --root <dir>`. `--app-origin` is required for a fresh session. An ended session that still holds un-acknowledged batches also resumes, with its tokens, so the `final` batch survives a restart; `wait` on such a stopped root exits 2 ("not running; resume"), never 1, while batches remain. An ended and drained root is not resumed: `start` mints fresh tokens and rotates the log. Concurrent `start` calls for one root are serialized by `state/start.lock`. Fresh tokens are minted only when there is no state file, or the session ended and drained (`stop`, or `start` on such a root). | 0 |
109
+ | `status --root <dir>` | Prints `{ status, url?, port?, session_ended, board }` from `state/` without contacting the server. | 0 |
110
+ | `stop --root <dir>` | Stops the server, invalidates both tokens, deletes `state/batches/`, keeps `state/log/`. | 0 |
111
+ | `wait --root <dir>` | Reads the agent token from `state/session.json`, long-polls `/wait` with it as a bearer header, prints one envelope. | 0 batch; 1 session ended with nothing held (`{ "status": "session-ended" }`); 2 error; 3 another process holds the wake (`{ "status": "wait-taken" }`, do not stop the endpoint) |
112
+ | `replay --root <dir> --profile <name> --to <endpoint> --token <page token> [--log <log-ended-<stamp>\|dir>]` | Re-emits `state/log/` to another endpoint under an evidence profile, as a fresh session over the page routes. `--log` names an earlier session on the same root (a rotated `state/log-ended-<stamp>/` by name, or any log directory by path); without it the current `state/log/` is replayed. Batches are sized in encoded bytes against the 64 KB cap; a lone envelope is posted bare so anything the source accepted fits the target. Under any profile that keeps frames, unit `evidence.frame_ids` is pruned to the frames actually re-emitted. | 0 |
113
+
114
+ `--trust-proxy` names the TLS-terminating proxy or tunnel addresses whose `X-Forwarded-Proto` the mint route may believe; a tunnel client on the same host connects over loopback and needs no entry.
115
+
116
+ Owner death (`--owner-pid`) and the idle timeout (default 30 min, `CE_LIVE_IDLE_TIMEOUT_MS`) stop the process without ending the session; `state/` stays intact and `start --root` resumes. Only `/session/end`, `stop`, or a closed tab ends a session: when the bound page last reported `stream_state: unloading` (or its stream went lost) and no stream is open, a request with a different `session_id` ends that session (`{ "kind": "session_closed_by_page" }` in `agent.ndjson`) and, once it drains, opens its own. A reload keeps its `session_id` and resumes instead. `/session/end` ends the board, not the tokens: both stay valid until `stop` (or a fresh, non-resume `start` on the ended root), per I4/KTD18. While the process is up, the same link can run another session: once the ended one drains, the first page request with a new `session_id` rotates `state/log/` to `state/log-ended-<stamp>/`, clears `state/batches/`, resets the board to that id, persists `ended: false`, and logs `{ "kind": "session_opened", "session_id" }` to `agent.ndjson`. Once the `final` batch is acknowledged and nothing is held, `GET /wait` with the still-valid agent token answers `410 { "status": "session-ended" }` and the `wait` CLI exits 1; `GET /status`, `POST /checkpoints/:id/ack` (idempotent) and the `status` CLI keep working until `stop`, so an agent that crashes between the final ack and `stop` can still resume, inspect, and stop the root. After a new board opens, `wait` parks for and serves that session, and `status` reports `session_ended: false`. `stop` retires both tokens.
117
+
118
+ ### Run directory
119
+
120
+ ```
121
+ state/ 0700
122
+ session.json 0600 { page_token, agent_token, url, app_origin, host, port, pid, owner_pid, ended, root, log_dir }
123
+ board.json 0600 units, annotations, answers, checkpoints, acked_checkpoint_ids, acked_seq, page state
124
+ brief.md session brief the skill writes before start (read at mint, max 3000 chars)
125
+ server.pid, server.log
126
+ batches/<checkpoint>.json un-acknowledged wake envelopes
127
+ log/events.ndjson every accepted envelope, in seq order (frames reference log/frames/<seq>-<id>.jpg; a dropped frame has frame_file null)
128
+ log/frames/<seq>-<id>.jpg
129
+ log/agent.ndjson acknowledgments, statuses, asks, mints, and every refused request as
130
+ { kind: "rejected", method, route, status, reason, seq? } (never secrets:
131
+ no credential, no query string, no request header or body)
132
+ log/archive.{zip,json,bin} the page's archive from /session/end
133
+ ```
134
+
135
+ ### Known gaps: crash windows between durable writes
136
+
137
+ The helper persists its state as separate synchronous writes (`board.json`, `session.json`, `events.ndjson`, `batches/*.json`, `log/frames/*`). A handler that fails between two of them rolls back; a process that is killed between two of them cannot, and leaves the pair disagreeing until the next start. These windows are accepted as known gaps (decided 2026-09-19 on #1726): each is a few microseconds wide, each consequence is a duplicate or a missed announcement the agent and page already tolerate, and closing them for good means one atomically written state file with an idempotent-by-seq log, which is its own change. A review finding of this class is answered with this list, not patched.
138
+
139
+ | Window (killed after … before …) | Consequence | Why tolerated |
140
+ |---|---|---|
141
+ | The event log or frame file is appended … the board records the advanced `acked_seq` | The page retries the seq; the log holds two records for it and `replay` renumbers both | Units and annotations are keyed by id, so the board is not duplicated; a repeated transcript or telemetry record, or a repeated checkpoint or answer wake, is handled as a fresh batch |
142
+ | An `applied` notice is held for a page between stream connections … the page reconnects | The held notice is in memory only and is lost with the process | The unit's `applied` status still replays on connect; only the interviewer's spoken announcement of that moment is missed |
143
+ | The archive is renamed into place … the terminal board and session writes | The archive counts as live-session bytes; the page's retry can be refused 413 when the two do not fit the cap | Narrow window; `stop` and a fresh start recover the root, and the riffer sees the refusal at once rather than a silently lost archive |
144
+ | The `page_lost` batch is persisted … the board clears `watch_for_loss` | The restart re-arms the watch and may queue a second `page_lost` wake for one loss | The second wake carries no units; the agent acknowledges it and continues |
145
+
146
+ ### Evidence profiles (`replay --profile`)
147
+
148
+ | Profile | Frames | Annotations | Audio clips, telemetry |
149
+ |---|---|---|---|
150
+ | `anchors_transcript_only` | none | dropped | dropped |
151
+ | `strokes_composite` | `composite` only | kept | dropped |
152
+ | `everything` | all | kept | kept |
153
+
154
+ Dropping telemetry also drops the `click`/`navigation`/`network_request`/`console_error` envelopes themselves; under `strokes_composite`, unit `evidence.frame_ids` is pruned to the composite frames actually re-emitted.
155
+
156
+ ## Mint
157
+
158
+ `POST /mint` is proxied through, and owned by, the endpoint. The endpoint reads `OPENAI_API_KEY` from its own environment (`OPENAI_BASE_URL` overrides the upstream, `OPENAI_REALTIME_MODEL` and `OPENAI_REALTIME_VOICE` the session). A request carrying `X-Riffrec-OpenAI-Key` still has that key win over the environment for that one mint, kept for compatibility with a page that sends it; the skill does not offer a paste path (a long-lived key persisted in the app origin's storage is readable by anything running there), the pinned riffrec build has no key box, and the endpoint never stores or logs the header. The endpoint appends `state/brief.md` to the persona after scanning it for secret shapes (known key prefixes, `KEY=`/`TOKEN=`/`SECRET=`-style assignments, URLs with credential parameters or userinfo), and calls `POST /v1/realtime/client_secrets` with:
159
+
160
+ ```json
161
+ {
162
+ "expires_after": { "anchor": "created_at", "seconds": 600 },
163
+ "session": {
164
+ "type": "realtime",
165
+ "model": "gpt-realtime",
166
+ "instructions": "<persona>\n\n[SESSION BRIEF]\n<brief>",
167
+ "tools": [ "<the five tools below>" ],
168
+ "tool_choice": "auto",
169
+ "audio": {
170
+ "input": {
171
+ "transcription": { "model": "gpt-4o-mini-transcribe" },
172
+ "turn_detection": { "type": "semantic_vad", "create_response": true, "interrupt_response": true }
173
+ },
174
+ "output": { "voice": "marin" }
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ The endpoint never logs request headers or mint bodies and never persists the minted `client_secret`.
181
+
182
+ ## Interviewer tools
183
+
184
+ Five flat function tools, riffrec `LIVE_TOOLS` (`src/live/tools.ts`) copied verbatim into `INTERVIEWER_TOOLS` and pinned by `tests/fixtures/ce-polish-live/live-tools.json`. No tool emits checkpoints or reports state: the page owns all timing and tells the interviewer about page-side facts as text conversation items. No tool *parameter* carries image content: `look_at_screen` asks the page for a screenshot, and the page attaches it as an image conversation item before the tool result.
185
+
186
+ | Tool | Parameters | Purpose |
187
+ |---|---|---|
188
+ | `record_unit` | `statement`, `anchors: string[]` (an anchor id from a `[PAGE]` note, "this"/"here" meaning the most recent one, or the riffer's own words for the element), `transcript_excerpt` | Record one requested change; once per change; never for questions, thinking aloud, or short utterances without a change verb. |
189
+ | `update_unit` | `unit_id`, `statement?`, `anchors_add?: string[]` | Refine a unit the interviewer recorded; rejected once it has left `initial`, in which case the refinement is recorded as a new unit. |
190
+ | `withdraw_unit` | `unit_id`, `reason?` | Retract a unit the riffer took back; never because the interviewer is unsure. |
191
+ | `relay_answer` | `unit_id`, `answer_text` | Relay the riffer's answer to a question that arrived from the endpoint; not for the interviewer's own clarifying questions. |
192
+ | `look_at_screen` | `reason?` | See the riffer's screen right now: for how something looks when the announced anchors do not settle it, for "can you see my screen?", and when asked to look. Never more than once per riffer turn, never to browse. |
193
+
194
+ Every parameter schema carries `additionalProperties: false`.
195
+
196
+ ### `look_at_screen`
197
+
198
+ When the model calls it, the page grabs the current view (or takes the latest buffered frame when it is under 500 ms old), sends a `conversation.item.create` with a `user` message whose content is `[{ type: "input_image", image_url: "data:image/jpeg;base64,…" }, { type: "input_text", text: "<caption>" }]`, then the `function_call_output`, then `response.create` (deferred until the calling response's `response.done` when one is active). Grabs are downscaled to 1280 px wide. Result shapes:
199
+
200
+ | Result | Meaning |
201
+ |---|---|
202
+ | `{ ok: true, frame_id, route, age_ms, fresh }` | The screenshot precedes this result in the conversation. `fresh` is false when the latest buffered frame stood in. |
203
+ | `{ ok: false, reason: "no_frame", detail }` | The screen is not shared or capture is paused. |
204
+ | `{ ok: false, reason: "frames_disabled", detail }` | The evidence profile is `frames: "none"`; nothing visual leaves the page. |
205
+ | `{ ok: false, reason: "send_failed", detail }` | The image item could not be sent. |
206
+
207
+ The frame is buffered like a gesture frame (the next unit attaches to it) and released to this endpoint as an ordinary `frame` envelope even under `frames: "one"`. Nothing on the wire changes for frames the interviewer sees. Image input needs nothing in the session body the mint sends: the Realtime API takes `input_image` content parts on any user message and its session configuration carries only `output_modalities`.
208
+
209
+ ### Page → interviewer announcements
210
+
211
+ Page-side facts reach the interviewer as `system`-role `input_text` items, never as tool calls, and are held while a response is active. The persona keys on these shapes:
212
+
213
+ | Shape | When |
214
+ |---|---|
215
+ | `[PAGE] The riffer clicked <description> (anchor id: anchor_NNNN).` | Every click on the host page. `<description>` is `<accessible name or tag>[ with text "<visible text, ≤80 chars>"][ in component <Component>] (selector <css>, route </path>)`. Repeated clicks on the same selector inside 1 s refresh the anchor but send no second note. Clicks on riffrec's own panel are not announced. |
216
+ | `[PAGE] The riffer drew on <description> (anchor id: anchor_NNNN).` | A completed stroke. |
217
+ | `[PAGE] The riffer pinned <description> (anchor id: anchor_NNNN).` | A pin. |
218
+ | `[PAGE] Screenshot of the riffer's current view on </path>, <attached because they just clicked there \| attached because they just drew there \| attached because they referred to something on screen \| captured just now \| captured N s ago> (frame id: frame_NNNN). The riffrec panel docked at the top right is not part of the app.` | The caption of an image item: proactive (first three) or `look_at_screen` (last two). Proactive frames are rate-limited to one per 5 s, a `look_at_screen` counts against the same limit, and speech triggers only when the transcript contains a deictic or visual word. |
219
+ | `[PAGE] The riffer muted their microphone; expect silence.` / `… unmuted their microphone.` | Mute toggles. |
220
+ | `[PAGE] The page lost its connection to the coding agent and is buffering; units still land on the board.` / `… reconnected …` | Stream state changes. |
221
+ | `[ENDPOINT QUESTION] The coding agent asks about unit <id>: "<question>" …` | An `ask` from this endpoint, voiced at the next pause. |
222
+ | `[RECONNECT] Your connection was replaced mid-session. …` | The re-seed on a replacement connection. |
223
+
224
+ Anchor ids are `anchor_NNNN`, minted per connection in announcement order; the most recent one is what "this", "here", and "that" resolve to when a `record_unit` reference matches nothing else within 8 s.
225
+
226
+ ### Reconciliation after connect
227
+
228
+ The page answers the tool calls and attaches the screenshots, so once the data channel opens it reads the session this endpoint minted (`session.created`) and sends one `session.update` only when something it must be able to answer is missing: tools in `LIVE_TOOLS` the mint did not carry are appended (tools the endpoint did define are kept verbatim, in the endpoint's order); a persona without the `[SCREEN CONTEXT]` section gets it appended; a session with no riffrec tool at all gets the default persona and all of `LIVE_TOOLS`. This helper copies both verbatim and is never patched. An endpoint persona must not tell the interviewer it cannot see the screen: the page contradicts that with notes and frames.
229
+
230
+ ## Default persona
231
+
232
+ Verbatim riffrec `DEFAULT_INTERVIEWER_INSTRUCTIONS` (`src/live/realtime/persona.ts`), pinned by `tests/fixtures/ce-polish-live/interviewer-instructions.txt`; the mint appends `\n\n[SESSION BRIEF]\n<brief>` when `state/brief.md` exists. The closing `[SCREEN CONTEXT]` section is the marker the page checks for.
233
+
234
+ > You are the riffrec interviewer: a calm, terse product partner listening to a designer or developer (the riffer) talk through changes they want while they click and draw on their own running app. The page tells you what they click, draw on, and pin, and shows you the screen when you ask for it; the last section says how.
235
+ >
236
+ > Your job is to turn what the riffer says into units of change on a shared board, one unit per requested change, using the record_unit tool. A sentence that asks for three things becomes three record_unit calls. Never call record_unit for questions, thinking aloud, praise, or utterances shorter than three words without a change verb.
237
+ >
238
+ > Ask immediately, in one short sentence, when the target element or the intended value is ambiguous: which element, which side, what color, how much. Otherwise stay quiet and let the riffer keep talking. Do not narrate, summarize, or confirm each unit aloud; the board already shows it.
239
+ >
240
+ > Never invent anchors. Use only the anchor ids the page announced or the element references the riffer named. When the riffer names no element and no anchor was announced, record the unit with an empty anchors list.
241
+ >
242
+ > When the riffer takes back a change, call withdraw_unit and acknowledge it aloud in a few words. When they refine a change already on the board, call update_unit; if it is rejected because the unit was already picked up, record the refinement as a new unit.
243
+ >
244
+ > When a note marked [ENDPOINT QUESTION] arrives, read the question to the riffer in your own words at the next pause and, once they answer, call relay_answer with their answer for that unit. Never answer such a question yourself.
245
+ >
246
+ > Keep every spoken turn under two sentences. Speak the riffer's language.
247
+ >
248
+ > [SCREEN CONTEXT]
249
+ > The page keeps you informed about the screen, and this section is authoritative about it: it supersedes any earlier statement that you cannot see the page or must not claim to.
250
+ > Every click the riffer makes arrives as a system note tagged [PAGE] that names the element (its component, visible text, selector, and route) and gives it an anchor id. Drawings and pins arrive the same way. The most recent note is what "this", "here", and "that" refer to: put its anchor id in record_unit's anchors, and never ask which element they mean when a note arrived within the last few seconds.
251
+ > You can also see the screen. Call look_at_screen when the riffer refers to how something looks, asks whether you can see their screen, or asks you to look; the page attaches a screenshot of the current view and you may then describe or refer to what is in it. The riffrec panel docked at the top right is not part of the app. Never say you cannot see the screen: if no frame is available the tool result says so, and you ask the riffer to describe what they see instead.
252
+
253
+ The executable copies of the tools and persona live in `scripts/live-endpoint.js`; change the script, this file, and the two fixtures together, from riffrec's source at the pinned commit.
@@ -0,0 +1,67 @@
1
+ # Prepare the live polish loop
2
+
3
+ This reference owns checkout safety, server startup, reachability, and browser handoff. It does not own the user's iterative polish decisions.
4
+
5
+ ## Resolve the workspace
6
+
7
+ If the user named a PR or branch, first locate whether its branch is already checked out in a worktree. Enter that existing worktree when the harness can; if it cannot, report the blocker and stop. Only use the harness's checkout capability in the current workspace when no other worktree owns the target. With no argument, stay in the current checkout.
8
+
9
+ Confirm the resulting branch is neither the repository's default branch nor detached. Report and stop when a safe feature-branch workspace cannot be reached; do not create another worktree behind the harness or move uncommitted user changes.
10
+
11
+ ## Resolve the start command
12
+
13
+ The commands below execute scripts bundled with this skill. For every self-contained shell call, set `SKILL_DIR` to the absolute directory containing the loaded `ce-polish` `SKILL.md`; shell state does not carry between calls.
14
+
15
+ First inspect the repo-root launch configuration:
16
+
17
+ ```bash
18
+ SKILL_DIR="<absolute path of the directory containing this SKILL.md>";
19
+ bash "$SKILL_DIR/scripts/read-launch-json.sh"
20
+ ```
21
+
22
+ Resolve one startup tuple: command, working directory, environment, and port. A selected launch configuration supplies every usable fact it declares: `runtimeExecutable` plus optional `runtimeArgs` form the command, `cwd` defaults to the repository root, `env` augments the inherited environment, and `port` must be numeric. Preserve those facts while resolving only what remains unknown. When all four facts are usable, the tuple is complete: skip classification, recipe loading, package-manager resolution, and port resolution, then continue to startup. Ambiguous declarations remain in disambiguation: show their names, ask the user to choose, and rerun with that name. Any operational failure or unresolved tuple fact blocks startup and must be reported.
23
+
24
+ Run project classification only when an unresolved command or port requires a project type. Classify the selected working directory when a launch configuration supplied one; otherwise classify the repository root. Omit the path argument for the repository root:
25
+
26
+ ```bash
27
+ SKILL_DIR="<absolute path of the directory containing this SKILL.md>";
28
+ bash "$SKILL_DIR/scripts/detect-project-type.sh" "<project-root>"
29
+ ```
30
+
31
+ `<type>` means the classification root; `<type>@<relative-dir>` means that directory under the classification root. Ask the user to choose when the output is `multiple` or `multiple:...`. For `unknown`, ask only for the unresolved tuple facts and do not guess.
32
+
33
+ Read `references/dev-server-<base-type>.md` only when the command remains unresolved after a supported classification. If that recipe requires a package-manager executable to complete the command, resolve it in the classified project root rather than guessing:
34
+
35
+ ```bash
36
+ SKILL_DIR="<absolute path of the directory containing this SKILL.md>";
37
+ bash "$SKILL_DIR/scripts/resolve-package-manager.sh" "<project-root>"
38
+ ```
39
+
40
+ Run the port resolver only while the port remains unresolved, using the detected type without replacing a selected command, working directory, or environment:
41
+
42
+ ```bash
43
+ SKILL_DIR="<absolute path of the directory containing this SKILL.md>";
44
+ bash "$SKILL_DIR/scripts/resolve-port.sh" "<project-root>" --type <base-type>
45
+ ```
46
+
47
+ Startup may proceed only when the tuple has a usable command, working directory, environment, and numeric port. If a classifier, recipe, or resolver fails operationally or leaves its required fact unknown, report that blocker; do not substitute a plausible value. After supported auto-detection supplies a missing fact, offer once to save the completed tuple to the repo-root launch configuration; write it only when the user accepts, after reading `references/launch-json-schema.md` and any recipe used.
48
+
49
+ ## Start and hand off
50
+
51
+ Inspect the chosen port and select exactly one intended server instance before handoff. Reuse a process already serving that port only when evidence identifies it as the intended project server. Only when no intended instance is selected may the resolved command be launched in the background with the project's working directory and environment; that process becomes the selected instance. Keep its process or session handle, and write its output under a directory created with `mktemp -d "${TMPDIR:-/tmp}/ce-polish-XXXXXX"`.
52
+
53
+ An occupied port that cannot be attributed to the intended project server remains an unresolved collision. Ask the user whether to stop that process, choose another port, or stop this run; never kill it or launch past it.
54
+
55
+ Resolve the selected instance's actual URL before handoff. The resolved port seeds `http://localhost:<port>` as the default candidate, but server output or a user correction replaces that candidate when it identifies a different URL. Attribute successful reachability at the resolved actual URL to the selected instance by probing for up to 30 seconds; a response from another process is not success.
56
+
57
+ - **Reachable:** use the browser-opening capability already exposed by the active harness with the verified actual URL. If it has none or the handoff fails, print that URL; browser handoff is a convenience, not a gate.
58
+ - **Not reachable:** show diagnostics derived from the selected instance. Include the last 20 log lines only when this run launched it and owns those logs. Ask whether to correct the server URL or start configuration, or stop.
59
+
60
+ Do not continue into the polish loop unless reachability is attributed to the selected instance.
61
+
62
+ Tell the user:
63
+
64
+ ```text
65
+ Dev server running on <verified-actual-url>
66
+ Browse the feature and tell me what could be better.
67
+ ```
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # detect-project-type.sh — inspect signature files at a project root (and, if
4
+ # no root match is found, probe shallow subdirectories) to emit a project-type
5
+ # identifier on stdout.
6
+ #
7
+ # Usage:
8
+ # detect-project-type.sh [path]
9
+ #
10
+ # Arguments:
11
+ # path (optional) — project root to inspect. Relative paths resolve from the
12
+ # repository root. Defaults to the repository root. The
13
+ # resolved path must remain inside the repository.
14
+ #
15
+ # Output grammar (one line on stdout):
16
+ #
17
+ # <type> — single signature match at root
18
+ # e.g. "next", "rails", "vite"
19
+ #
20
+ # <type>@<relative-dir> — single monorepo hit (no root match)
21
+ # e.g. "next@apps/web"
22
+ #
23
+ # multiple — two or more disjoint root signatures
24
+ # (caller must prompt for disambiguation)
25
+ #
26
+ # multiple:<type>@<dir>,<type>@<dir> — multiple monorepo hits (no root match)
27
+ # e.g. "multiple:next@apps/web,rails@apps/api"
28
+ #
29
+ # unknown — no signatures found at root or in probe
30
+ #
31
+ # Supported root types: rails, next, vite, nuxt, astro, remix, sveltekit, procfile
32
+ #
33
+ # Monorepo probe:
34
+ # Runs only when root detection finds ZERO matches. Searches subdirectories
35
+ # up to depth 3 (e.g. services/api/server/vite.config.ts) for framework
36
+ # signature files. Deeper nesting is ignored to avoid false positives.
37
+ #
38
+ # Excluded directories (not real project roots):
39
+ # node_modules .git vendor dist build coverage .next .nuxt
40
+ # .svelte-kit .turbo tmp fixtures
41
+ #
42
+ # `multiple` vs `rails`: Rails apps commonly ship a Procfile.dev alongside
43
+ # bin/dev. To avoid treating every Rails app as a monorepo, the `rails`
44
+ # signature takes precedence over a bare `procfile` match. `multiple` is
45
+ # reserved for genuine disambiguation cases (e.g., Rails + Next, Next + Vite).
46
+
47
+ set -u
48
+
49
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
50
+ if [ -z "$REPO_ROOT" ]; then
51
+ echo "ERROR: not in a git repository" >&2
52
+ exit 1
53
+ fi
54
+
55
+ REPO_ROOT=$(cd "$REPO_ROOT" 2>/dev/null && pwd -P)
56
+ if [ -z "$REPO_ROOT" ]; then
57
+ echo "ERROR: cannot resolve repo root" >&2
58
+ exit 1
59
+ fi
60
+
61
+ PROJECT_ROOT="${1:-$REPO_ROOT}"
62
+ case "$PROJECT_ROOT" in
63
+ /*) ;;
64
+ *) PROJECT_ROOT="$REPO_ROOT/$PROJECT_ROOT" ;;
65
+ esac
66
+
67
+ if [ ! -d "$PROJECT_ROOT" ]; then
68
+ echo "ERROR: path does not exist: $PROJECT_ROOT" >&2
69
+ exit 1
70
+ fi
71
+
72
+ if ! PROJECT_ROOT=$(cd "$PROJECT_ROOT" 2>/dev/null && pwd -P); then
73
+ echo "ERROR: cannot resolve project root" >&2
74
+ exit 1
75
+ fi
76
+ case "$PROJECT_ROOT" in
77
+ "$REPO_ROOT"|"$REPO_ROOT"/*) ;;
78
+ *)
79
+ echo "ERROR: path must stay inside repo root: $PROJECT_ROOT" >&2
80
+ exit 1
81
+ ;;
82
+ esac
83
+
84
+ cd "$PROJECT_ROOT" || { echo "ERROR: cannot cd to project root" >&2; exit 1; }
85
+
86
+ MATCHES=()
87
+
88
+ # Rails: bin/dev AND Gemfile together. A Gemfile alone (or bin/dev alone) is
89
+ # insufficient -- plenty of gems have Gemfiles without bin/dev, and bin/dev
90
+ # may exist in non-Rails projects.
91
+ if [ -f "bin/dev" ] && [ -f "Gemfile" ]; then
92
+ MATCHES+=("rails")
93
+ fi
94
+
95
+ # Next.js
96
+ if [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "next.config.ts" ] || [ -f "next.config.cjs" ]; then
97
+ MATCHES+=("next")
98
+ fi
99
+
100
+ # Vite
101
+ if [ -f "vite.config.js" ] || [ -f "vite.config.ts" ] || [ -f "vite.config.mjs" ] || [ -f "vite.config.cjs" ]; then
102
+ MATCHES+=("vite")
103
+ fi
104
+
105
+ # Nuxt
106
+ if [ -f "nuxt.config.js" ] || [ -f "nuxt.config.mjs" ] || [ -f "nuxt.config.ts" ]; then
107
+ MATCHES+=("nuxt")
108
+ fi
109
+
110
+ # Astro
111
+ if [ -f "astro.config.js" ] || [ -f "astro.config.mjs" ] || [ -f "astro.config.ts" ]; then
112
+ MATCHES+=("astro")
113
+ fi
114
+
115
+ # Remix (classic — Remix on Vite uses vite.config.ts, detected as vite)
116
+ if [ -f "remix.config.js" ] || [ -f "remix.config.ts" ]; then
117
+ MATCHES+=("remix")
118
+ fi
119
+
120
+ # SvelteKit
121
+ if [ -f "svelte.config.js" ] || [ -f "svelte.config.mjs" ] || [ -f "svelte.config.ts" ]; then
122
+ MATCHES+=("sveltekit")
123
+ fi
124
+
125
+ # Procfile / Overmind / Foreman — only if we didn't already detect rails
126
+ if [ ${#MATCHES[@]} -eq 0 ] || [ "${MATCHES[0]}" != "rails" ]; then
127
+ if [ -f "Procfile" ] || [ -f "Procfile.dev" ]; then
128
+ MATCHES+=("procfile")
129
+ fi
130
+ fi
131
+
132
+ # ── Root result ──────────────────────────────────────────────────────────────
133
+ case ${#MATCHES[@]} in
134
+ 0)
135
+ # No root match — run monorepo probe (shallow find, depth <= 3).
136
+ ;;
137
+ 1)
138
+ echo "${MATCHES[0]}"
139
+ exit 0
140
+ ;;
141
+ *)
142
+ echo "multiple"
143
+ exit 0
144
+ ;;
145
+ esac
146
+
147
+ # ── Monorepo probe ─────────────────────────────────────────────────────────
148
+ # When root detection returns zero matches, descend up to depth 3 looking for
149
+ # framework signatures in workspace directories. Common layouts:
150
+ # apps/web/next.config.js (depth 2)
151
+ # packages/frontend/vite.config.ts (depth 2)
152
+ # services/api/server/vite.config.ts (depth 3)
153
+ #
154
+ # Exclusion list: directories that ship framework configs as fixtures or build
155
+ # output, not as real project roots.
156
+
157
+ EXCLUDE_DIRS="node_modules .git vendor dist build coverage .next .nuxt .svelte-kit .turbo tmp fixtures"
158
+ EXCLUDE_ARGS=""
159
+ for d in $EXCLUDE_DIRS; do
160
+ EXCLUDE_ARGS="$EXCLUDE_ARGS -path './$d' -prune -o -path '*/$d' -prune -o"
161
+ done
162
+
163
+ # Signature file patterns to look for
164
+ SIGNATURE_PATTERNS=(
165
+ "next.config.js" "next.config.mjs" "next.config.ts" "next.config.cjs"
166
+ "vite.config.js" "vite.config.ts" "vite.config.mjs" "vite.config.cjs"
167
+ "nuxt.config.js" "nuxt.config.mjs" "nuxt.config.ts"
168
+ "astro.config.js" "astro.config.mjs" "astro.config.ts"
169
+ "remix.config.js" "remix.config.ts"
170
+ "svelte.config.js" "svelte.config.mjs" "svelte.config.ts"
171
+ )
172
+
173
+ # Build the find -name arguments
174
+ NAME_ARGS=""
175
+ for i in "${!SIGNATURE_PATTERNS[@]}"; do
176
+ if [ "$i" -gt 0 ]; then
177
+ NAME_ARGS="$NAME_ARGS -o"
178
+ fi
179
+ NAME_ARGS="$NAME_ARGS -name '${SIGNATURE_PATTERNS[$i]}'"
180
+ done
181
+
182
+ # Run find. Use eval because the dynamically built arguments contain quoted
183
+ # strings that must be expanded by the shell.
184
+ FOUND_FILES=$(eval "find . -maxdepth 4 $EXCLUDE_ARGS \\( $NAME_ARGS \\) -print" 2>/dev/null | sort)
185
+
186
+ # Also check for Rails signature (bin/dev + Gemfile in the same subdir)
187
+ RAILS_HITS=""
188
+ # Find all Gemfiles at depth <= 3, check each dir for bin/dev
189
+ while IFS= read -r gemfile; do
190
+ [ -z "$gemfile" ] && continue
191
+ gdir=$(dirname "$gemfile")
192
+ if [ -f "$gdir/bin/dev" ]; then
193
+ RAILS_HITS="$RAILS_HITS
194
+ $gdir"
195
+ fi
196
+ done < <(eval "find . -maxdepth 4 $EXCLUDE_ARGS -name 'Gemfile' -print" 2>/dev/null)
197
+
198
+ # Parse found files into (type, relative-dir) pairs. Use a newline-delimited
199
+ # string instead of an associative array so the script works on macOS's default
200
+ # Bash 3.2 as well as newer Bash versions.
201
+ MONO_HITS=""
202
+
203
+ add_mono_hit() {
204
+ hit="$1"
205
+ if printf '%s\n' "$MONO_HITS" | grep -Fxq "$hit"; then
206
+ return 0
207
+ fi
208
+ MONO_HITS="${MONO_HITS}
209
+ ${hit}"
210
+ }
211
+
212
+ if [ -n "$FOUND_FILES" ]; then
213
+ for f in $FOUND_FILES; do
214
+ [ -z "$f" ] && continue
215
+ fname=$(basename "$f")
216
+ fdir=$(dirname "$f")
217
+ # Normalize dir: strip leading ./
218
+ fdir="${fdir#./}"
219
+
220
+ # Enforce depth cap of 3: count slashes in the relative path of the file.
221
+ # A file at apps/web/next.config.js has dir apps/web (1 slash = depth 2).
222
+ # A file at a/b/c/d/next.config.js has dir a/b/c/d (3 slashes = depth 4 = too deep).
223
+ # We want maxdepth 3 for the directory, meaning at most 2 slashes in fdir.
224
+ slash_count=$(echo "$fdir" | tr -cd '/' | wc -c | tr -d ' ')
225
+ if [ "$slash_count" -gt 2 ]; then
226
+ continue
227
+ fi
228
+
229
+ case "$fname" in
230
+ next.config.*) ftype="next" ;;
231
+ vite.config.*) ftype="vite" ;;
232
+ nuxt.config.*) ftype="nuxt" ;;
233
+ astro.config.*) ftype="astro" ;;
234
+ remix.config.*) ftype="remix" ;;
235
+ svelte.config.*) ftype="sveltekit" ;;
236
+ *) continue ;;
237
+ esac
238
+
239
+ # Skip root hits (those would have been caught by root detection)
240
+ if [ "$fdir" = "." ]; then continue; fi
241
+
242
+ add_mono_hit "${ftype}@${fdir}"
243
+ done
244
+ fi
245
+
246
+ # Add Rails monorepo hits
247
+ if [ -n "$RAILS_HITS" ]; then
248
+ for rdir in $RAILS_HITS; do
249
+ [ -z "$rdir" ] && continue
250
+ rdir="${rdir#./}"
251
+ if [ "$rdir" != "." ] && [ -n "$rdir" ]; then
252
+ # Enforce depth cap for Rails hits too
253
+ slash_count=$(echo "$rdir" | tr -cd '/' | wc -c | tr -d ' ')
254
+ if [ "$slash_count" -le 2 ]; then
255
+ add_mono_hit "rails@${rdir}"
256
+ fi
257
+ fi
258
+ done
259
+ fi
260
+
261
+ MONO_HITS=$(printf '%s\n' "$MONO_HITS" | sed '/^$/d' | sort)
262
+ MONO_COUNT=$(printf '%s\n' "$MONO_HITS" | sed '/^$/d' | wc -l | tr -d ' ')
263
+
264
+ case $MONO_COUNT in
265
+ 0)
266
+ echo "unknown"
267
+ ;;
268
+ 1)
269
+ # Single monorepo hit: emit type@cwd
270
+ printf '%s\n' "$MONO_HITS"
271
+ ;;
272
+ *)
273
+ # Multiple hits: emit multiple:type1@cwd1,type2@cwd2,...
274
+ result=$(printf '%s\n' "$MONO_HITS" | paste -sd, -)
275
+ echo "multiple:$result"
276
+ ;;
277
+ esac