@armanckeser/downstream 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.
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/SKILL.md +150 -0
- package/bin/downstream.mjs +19 -0
- package/dist/cli/downstream.js +543 -0
- package/dist/domain/agent.js +3 -0
- package/dist/domain/approval.js +190 -0
- package/dist/domain/delivery.js +21 -0
- package/dist/domain/home.js +1 -0
- package/dist/domain/inbox.js +245 -0
- package/dist/domain/model.js +180 -0
- package/dist/domain/next-move.js +34 -0
- package/dist/domain/pr-detail.js +45 -0
- package/dist/server/actions.js +564 -0
- package/dist/server/agent-host.js +230 -0
- package/dist/server/analyze/git.js +161 -0
- package/dist/server/analyze/graph.js +403 -0
- package/dist/server/analyze/py-graph.js +141 -0
- package/dist/server/analyze/py.js +346 -0
- package/dist/server/analyze/ts-decls.js +299 -0
- package/dist/server/analyze/ts-project.js +212 -0
- package/dist/server/gh.js +51 -0
- package/dist/server/github.js +264 -0
- package/dist/server/home-store.js +127 -0
- package/dist/server/home.js +325 -0
- package/dist/server/install.js +12 -0
- package/dist/server/main.js +228 -0
- package/dist/server/prompts.js +32 -0
- package/dist/server/publish.js +136 -0
- package/dist/server/repos.js +164 -0
- package/dist/server/stale.js +50 -0
- package/dist/server/store.js +269 -0
- package/dist/server/workspace.js +251 -0
- package/package.json +63 -4
- package/web/dist/assets/abap-CLvhMVsD.js +1 -0
- package/web/dist/assets/actionscript-3-Cs6JJXT5.js +1 -0
- package/web/dist/assets/ada-X3Y2mlq2.js +1 -0
- package/web/dist/assets/ahk-DwADkxyd.js +1 -0
- package/web/dist/assets/ahk2-_6rzW9BS.js +1 -0
- package/web/dist/assets/andromeeda-vGVdxbeo.js +1 -0
- package/web/dist/assets/angular-html-iITFzleb.js +1 -0
- package/web/dist/assets/angular-ts-qNGaWUZr.js +1 -0
- package/web/dist/assets/apache-U0d_L8uA.js +1 -0
- package/web/dist/assets/apex-KFGFyKwG.js +1 -0
- package/web/dist/assets/apl-DAcxbtpZ.js +1 -0
- package/web/dist/assets/applescript-CCn79oCD.js +1 -0
- package/web/dist/assets/ara-4CJ0cIlV.js +1 -0
- package/web/dist/assets/asciidoc-SCjQUq34.js +1 -0
- package/web/dist/assets/asm-Cmm7eHzH.js +1 -0
- package/web/dist/assets/astro-CxPQu5s8.js +1 -0
- package/web/dist/assets/aurora-x-CDeNXAV0.js +1 -0
- package/web/dist/assets/awk-BWXHIvNe.js +1 -0
- package/web/dist/assets/ayu-dark-DluEY0Gj.js +1 -0
- package/web/dist/assets/ayu-light-C3h-C4tm.js +1 -0
- package/web/dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
- package/web/dist/assets/ballerina-B7ZEbQpA.js +1 -0
- package/web/dist/assets/bat-qdxAdBWQ.js +1 -0
- package/web/dist/assets/beancount-C2IN4Vk8.js +1 -0
- package/web/dist/assets/berry-DKpUyyne.js +1 -0
- package/web/dist/assets/bibtex-Ci_nEsc7.js +1 -0
- package/web/dist/assets/bicep-CUHmPFLl.js +1 -0
- package/web/dist/assets/bird2-CYIUgYqB.js +1 -0
- package/web/dist/assets/blade-DJrnOqL4.js +1 -0
- package/web/dist/assets/bsl-_VjlB9mv.js +1 -0
- package/web/dist/assets/c-DJLzn8qa.js +1 -0
- package/web/dist/assets/c3-D0apG41V.js +1 -0
- package/web/dist/assets/cadence-CQ2zXKGN.js +1 -0
- package/web/dist/assets/cairo-DLTphjLi.js +1 -0
- package/web/dist/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
- package/web/dist/assets/catppuccin-latte-CtsyrtPq.js +1 -0
- package/web/dist/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
- package/web/dist/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
- package/web/dist/assets/chapel-BHYX99F3.js +1 -0
- package/web/dist/assets/clarity-CtfBBspg.js +1 -0
- package/web/dist/assets/clojure-DqKBuwfJ.js +1 -0
- package/web/dist/assets/cmake-Bj61d0ZC.js +1 -0
- package/web/dist/assets/cobol-C_LDam8n.js +1 -0
- package/web/dist/assets/codeowners-C8r90Shi.js +1 -0
- package/web/dist/assets/codeql-oeQT6MSM.js +1 -0
- package/web/dist/assets/coffee-CcuifkxZ.js +1 -0
- package/web/dist/assets/common-lisp-Cv5bFMCO.js +1 -0
- package/web/dist/assets/coq-CGaSfhwR.js +1 -0
- package/web/dist/assets/cpp-E7p80IG9.js +1 -0
- package/web/dist/assets/crystal-iYHODIir.js +1 -0
- package/web/dist/assets/csharp-dmMxLVLW.js +1 -0
- package/web/dist/assets/css-Bk6njqO4.js +1 -0
- package/web/dist/assets/csv-Dx-8-gkx.js +1 -0
- package/web/dist/assets/cue-CE9AQfxI.js +1 -0
- package/web/dist/assets/cypher-ClKdZ_lG.js +1 -0
- package/web/dist/assets/d-qD-0Kul2.js +1 -0
- package/web/dist/assets/dark-plus-Cs2F2srj.js +1 -0
- package/web/dist/assets/dart-CHZY9Xhz.js +1 -0
- package/web/dist/assets/dax-BkyTk9wS.js +1 -0
- package/web/dist/assets/desktop-Dlh5hvp9.js +1 -0
- package/web/dist/assets/diff-woXpYk--.js +1 -0
- package/web/dist/assets/docker-IyjqRm3v.js +1 -0
- package/web/dist/assets/dotenv-CuH5my32.js +1 -0
- package/web/dist/assets/dracula-BHWKrbxM.js +1 -0
- package/web/dist/assets/dracula-soft-5eyTD99u.js +1 -0
- package/web/dist/assets/dream-maker-DW3nJb8Q.js +1 -0
- package/web/dist/assets/edge-Ct7_SkpN.js +1 -0
- package/web/dist/assets/elixir-YTzSQL2h.js +1 -0
- package/web/dist/assets/elm-bNkwlHr8.js +1 -0
- package/web/dist/assets/emacs-lisp-DR2vtWaj.js +1 -0
- package/web/dist/assets/erb-uFBspDdG.js +1 -0
- package/web/dist/assets/erlang-Cphh6RMH.js +1 -0
- package/web/dist/assets/everforest-dark-sB-x3p7T.js +1 -0
- package/web/dist/assets/everforest-light-Df2xbC6M.js +1 -0
- package/web/dist/assets/fennel-DQxkIbk2.js +1 -0
- package/web/dist/assets/fish-BJitypiv.js +1 -0
- package/web/dist/assets/fluent-C03EYrpw.js +1 -0
- package/web/dist/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
- package/web/dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
- package/web/dist/assets/fsharp-D13ZGOAj.js +1 -0
- package/web/dist/assets/gdresource-xMC7jr7x.js +1 -0
- package/web/dist/assets/gdscript-DYvRD3hg.js +1 -0
- package/web/dist/assets/gdshader-CBce3t8t.js +1 -0
- package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/web/dist/assets/genie-CV2tkWYe.js +1 -0
- package/web/dist/assets/gherkin-DExj1W_8.js +1 -0
- package/web/dist/assets/git-commit-BSykSTBG.js +1 -0
- package/web/dist/assets/git-rebase-CSzaMu4k.js +1 -0
- package/web/dist/assets/github-dark-C-LZuMrd.js +1 -0
- package/web/dist/assets/github-dark-default-DXG-b-1a.js +1 -0
- package/web/dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
- package/web/dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
- package/web/dist/assets/github-light-EUqPIrTm.js +1 -0
- package/web/dist/assets/github-light-default-BXViO-2h.js +1 -0
- package/web/dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
- package/web/dist/assets/gleam-CSRkHgEL.js +1 -0
- package/web/dist/assets/glimmer-js-WrpQIag3.js +1 -0
- package/web/dist/assets/glimmer-ts-BkR7s81M.js +1 -0
- package/web/dist/assets/glsl-r6ahr8UN.js +1 -0
- package/web/dist/assets/gn-CAXYAgV-.js +1 -0
- package/web/dist/assets/gnuplot-7GGW24-e.js +1 -0
- package/web/dist/assets/go-rLFTqkRN.js +1 -0
- package/web/dist/assets/graphql-vcFzEvUM.js +1 -0
- package/web/dist/assets/groovy-CacY0gHj.js +1 -0
- package/web/dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
- package/web/dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
- package/web/dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
- package/web/dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
- package/web/dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
- package/web/dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
- package/web/dist/assets/hack-DV1TStQR.js +1 -0
- package/web/dist/assets/haml-BzEpEFnM.js +1 -0
- package/web/dist/assets/handlebars-Bw2ai2tn.js +1 -0
- package/web/dist/assets/haskell-D8IpX4py.js +1 -0
- package/web/dist/assets/haxe-C8oCnwJJ.js +1 -0
- package/web/dist/assets/hcl-Dh228itO.js +1 -0
- package/web/dist/assets/hjson-CxZEssPk.js +1 -0
- package/web/dist/assets/hlsl-Cvrh5tZx.js +1 -0
- package/web/dist/assets/horizon-CE9ld1lL.js +1 -0
- package/web/dist/assets/horizon-bright-DSNQnXHK.js +1 -0
- package/web/dist/assets/houston-CsvMBhTu.js +1 -0
- package/web/dist/assets/html-CVWJgEI4.js +1 -0
- package/web/dist/assets/html-derivative-kTFEcoss.js +1 -0
- package/web/dist/assets/http-DXC6PGgt.js +1 -0
- package/web/dist/assets/hurl-Bpd8P_ef.js +1 -0
- package/web/dist/assets/hxml-Dcm88srW.js +1 -0
- package/web/dist/assets/hy-CZbG8q4J.js +1 -0
- package/web/dist/assets/imba-DsUTQ-LC.js +1 -0
- package/web/dist/assets/index-DpYMsPxu.js +1673 -0
- package/web/dist/assets/index-Zh-Bd2u6.css +2 -0
- package/web/dist/assets/ini-B5eOa1yu.js +1 -0
- package/web/dist/assets/java-Bsmh9nXE.js +1 -0
- package/web/dist/assets/javascript-CeyaFN_P.js +1 -0
- package/web/dist/assets/jinja-BuqTDofy.js +1 -0
- package/web/dist/assets/jison-BYfmmaBZ.js +1 -0
- package/web/dist/assets/json-Bhl6cDM_.js +1 -0
- package/web/dist/assets/json5-BR5RXkoi.js +1 -0
- package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
- package/web/dist/assets/jsonl-CmCQp5Yx.js +1 -0
- package/web/dist/assets/jsonnet-CJTPZ8u_.js +1 -0
- package/web/dist/assets/jssm-DXw9l8Rf.js +1 -0
- package/web/dist/assets/jsx-BSUHjwDX.js +1 -0
- package/web/dist/assets/julia-CWrNF0wf.js +1 -0
- package/web/dist/assets/just-wLw7gWsD.js +1 -0
- package/web/dist/assets/kanagawa-dragon-Bozd7klA.js +1 -0
- package/web/dist/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
- package/web/dist/assets/kanagawa-wave-BML5mF2J.js +1 -0
- package/web/dist/assets/kdl-CsD5j6eV.js +1 -0
- package/web/dist/assets/kotlin-DhhofPvG.js +1 -0
- package/web/dist/assets/kusto-C7mF5XQf.js +1 -0
- package/web/dist/assets/laserwave-C_8bwKvT.js +1 -0
- package/web/dist/assets/latex-DyWaeH0P.js +1 -0
- package/web/dist/assets/lean-CewbzKMR.js +1 -0
- package/web/dist/assets/less-DVTAwKKz.js +1 -0
- package/web/dist/assets/light-plus-DVQuIRkW.js +1 -0
- package/web/dist/assets/liquid-DeCc5Rg2.js +1 -0
- package/web/dist/assets/llvm-DqHOMS-s.js +1 -0
- package/web/dist/assets/log-BNLmms1o.js +1 -0
- package/web/dist/assets/logo-Cluzi2Zq.js +1 -0
- package/web/dist/assets/lua-Ba-NltBp.js +1 -0
- package/web/dist/assets/luau-VGWVLoqX.js +1 -0
- package/web/dist/assets/make-Dixweg8N.js +1 -0
- package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
- package/web/dist/assets/marko-DGb2HMVj.js +1 -0
- package/web/dist/assets/material-theme-Bm3Qr25_.js +1 -0
- package/web/dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
- package/web/dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
- package/web/dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
- package/web/dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
- package/web/dist/assets/matlab-D7qyCx1q.js +1 -0
- package/web/dist/assets/mdc-CL7bhRQ-.js +1 -0
- package/web/dist/assets/mdx-DQZ5AkYe.js +1 -0
- package/web/dist/assets/mermaid-BxpfTIzt.js +1 -0
- package/web/dist/assets/min-dark-BSWPekZh.js +1 -0
- package/web/dist/assets/min-light-DDpmG2fV.js +1 -0
- package/web/dist/assets/mipsasm-BMqwQI7S.js +1 -0
- package/web/dist/assets/mojo-fg40_ntQ.js +1 -0
- package/web/dist/assets/monokai-BscAjiQZ.js +1 -0
- package/web/dist/assets/moonbit-D9W784E_.js +1 -0
- package/web/dist/assets/move-BYnGhZoM.js +1 -0
- package/web/dist/assets/narrat-_X_XdTYD.js +1 -0
- package/web/dist/assets/nextflow-CSgcYZcx.js +1 -0
- package/web/dist/assets/nextflow-groovy-D7IRt6Pp.js +1 -0
- package/web/dist/assets/nginx-CbyuJibN.js +1 -0
- package/web/dist/assets/night-owl-DhmEMT88.js +1 -0
- package/web/dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
- package/web/dist/assets/nim-C7LemgDR.js +1 -0
- package/web/dist/assets/nix-IvuFDN5E.js +1 -0
- package/web/dist/assets/nord-Cb4Vim4T.js +1 -0
- package/web/dist/assets/nsis-CjNLYCLg.js +1 -0
- package/web/dist/assets/nushell--0fT3PI1.js +1 -0
- package/web/dist/assets/objective-c-D1A_Heim.js +1 -0
- package/web/dist/assets/objective-cpp-BsSzOQcm.js +1 -0
- package/web/dist/assets/ocaml-O90oeIOV.js +1 -0
- package/web/dist/assets/odin-B1RWQWA5.js +1 -0
- package/web/dist/assets/one-dark-pro-C6YyR8OD.js +1 -0
- package/web/dist/assets/one-light-D7Lr4KcI.js +1 -0
- package/web/dist/assets/openscad-BUDT5pXO.js +1 -0
- package/web/dist/assets/org-wrbLazmP.js +1 -0
- package/web/dist/assets/pascal-4ZHwLPI5.js +1 -0
- package/web/dist/assets/perl-DarHXyEu.js +1 -0
- package/web/dist/assets/php--54M0-pj.js +1 -0
- package/web/dist/assets/pierre-dark-CpLgRqie.js +1 -0
- package/web/dist/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
- package/web/dist/assets/pierre-dark-soft-kZQmAZld.js +1 -0
- package/web/dist/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
- package/web/dist/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
- package/web/dist/assets/pierre-light-CoaEpmwp.js +1 -0
- package/web/dist/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
- package/web/dist/assets/pierre-light-soft-lWLdNTOI.js +1 -0
- package/web/dist/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
- package/web/dist/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
- package/web/dist/assets/pkl-ot-7Btpt.js +1 -0
- package/web/dist/assets/plastic-DQwYfKfQ.js +1 -0
- package/web/dist/assets/plsql-DGHpHOYJ.js +1 -0
- package/web/dist/assets/po-BiJDBrnU.js +1 -0
- package/web/dist/assets/poimandres-DRFjx7u4.js +1 -0
- package/web/dist/assets/polar-C7UOKdEL.js +1 -0
- package/web/dist/assets/postcss-BXeXVLqQ.js +1 -0
- package/web/dist/assets/powerquery-DNMTfnFr.js +1 -0
- package/web/dist/assets/powershell-QtSnmXcu.js +1 -0
- package/web/dist/assets/prisma-DMgNhnYS.js +1 -0
- package/web/dist/assets/prolog-iXnhIJG7.js +1 -0
- package/web/dist/assets/proto-DB4EqR-F.js +1 -0
- package/web/dist/assets/pug-CFrC1rDC.js +1 -0
- package/web/dist/assets/puppet-CDv2pdJW.js +1 -0
- package/web/dist/assets/purescript-9MfHhQsQ.js +1 -0
- package/web/dist/assets/python-gzcpVVnB.js +1 -0
- package/web/dist/assets/qml-Go4B46qX.js +1 -0
- package/web/dist/assets/qmldir-DCQb3MpD.js +1 -0
- package/web/dist/assets/qss-Fe1Jh2GI.js +1 -0
- package/web/dist/assets/r-Bpu17F3f.js +1 -0
- package/web/dist/assets/racket-DcIDlBhZ.js +1 -0
- package/web/dist/assets/raku-B3gFvitq.js +1 -0
- package/web/dist/assets/razor-CeZTEIIg.js +1 -0
- package/web/dist/assets/rbs-C0MpOrj4.js +1 -0
- package/web/dist/assets/red-CYlAq2b0.js +1 -0
- package/web/dist/assets/reg-CRGYupPL.js +1 -0
- package/web/dist/assets/regexp-D4K6CiTG.js +1 -0
- package/web/dist/assets/rel-BtDbiS_P.js +1 -0
- package/web/dist/assets/riscv-Ckw8ddFX.js +1 -0
- package/web/dist/assets/ron-VUp2lXgN.js +1 -0
- package/web/dist/assets/rose-pine-aMsaMD1D.js +1 -0
- package/web/dist/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
- package/web/dist/assets/rose-pine-moon-DO-qXg-I.js +1 -0
- package/web/dist/assets/rosmsg-CAekHB0j.js +1 -0
- package/web/dist/assets/rst-Dy2Bl1Fg.js +1 -0
- package/web/dist/assets/ruby-CNqr2Wuz.js +1 -0
- package/web/dist/assets/rust-DpiIf5JO.js +1 -0
- package/web/dist/assets/sas-B6MsvXFb.js +1 -0
- package/web/dist/assets/sass-DXrisJhu.js +1 -0
- package/web/dist/assets/scala-BiFt_7mu.js +1 -0
- package/web/dist/assets/scheme-DQCgrYNe.js +1 -0
- package/web/dist/assets/scss-CHuFWGi8.js +1 -0
- package/web/dist/assets/sdbl-bTVj8UrX.js +1 -0
- package/web/dist/assets/shaderlab-TOUzSsQk.js +1 -0
- package/web/dist/assets/shellscript-EEdMjEbs.js +1 -0
- package/web/dist/assets/shellsession-U_I5Q2cJ.js +1 -0
- package/web/dist/assets/slack-dark-DnToyrRv.js +1 -0
- package/web/dist/assets/slack-ochin-B2OO5cIa.js +1 -0
- package/web/dist/assets/smalltalk-BHpQb5nM.js +1 -0
- package/web/dist/assets/smithy-Dgaz7aur.js +1 -0
- package/web/dist/assets/snazzy-light-4G7pJPwS.js +1 -0
- package/web/dist/assets/solarized-dark-b97czJOk.js +1 -0
- package/web/dist/assets/solarized-light-DFPxpIYQ.js +1 -0
- package/web/dist/assets/solidity-Bj7E1mRn.js +1 -0
- package/web/dist/assets/soy-BnCBZkG3.js +1 -0
- package/web/dist/assets/sparql-D_iOobhT.js +1 -0
- package/web/dist/assets/splunk-BC2Px7Mm.js +1 -0
- package/web/dist/assets/sql-5dStcFSw.js +1 -0
- package/web/dist/assets/ssh-config-BgfXC-Er.js +1 -0
- package/web/dist/assets/stata-B5iSQdtS.js +1 -0
- package/web/dist/assets/stylus-B6D30XZt.js +1 -0
- package/web/dist/assets/surrealql-D4K5msa4.js +1 -0
- package/web/dist/assets/svelte-Dn-_7BgE.js +1 -0
- package/web/dist/assets/swift-CyEgAFGc.js +1 -0
- package/web/dist/assets/synthwave-84-nFMaYfgc.js +1 -0
- package/web/dist/assets/system-verilog-Dh5ihWbf.js +1 -0
- package/web/dist/assets/systemd-BxMlprV5.js +1 -0
- package/web/dist/assets/talonscript-CohzipZa.js +1 -0
- package/web/dist/assets/tasl-DMoTqEGO.js +1 -0
- package/web/dist/assets/tcl-CZd0xW_V.js +1 -0
- package/web/dist/assets/templ-BTe8yLW8.js +1 -0
- package/web/dist/assets/terraform-DswuEJGm.js +1 -0
- package/web/dist/assets/tex-BBG77enc.js +1 -0
- package/web/dist/assets/tokyo-night-oM2G3aXe.js +1 -0
- package/web/dist/assets/toml-CcmNWLt0.js +1 -0
- package/web/dist/assets/ts-tags-BzJXXnMD.js +1 -0
- package/web/dist/assets/tsv-sltzmVWM.js +1 -0
- package/web/dist/assets/tsx-Cj2inrAP.js +1 -0
- package/web/dist/assets/turtle-ByJddavk.js +1 -0
- package/web/dist/assets/twig-BH3HU2q4.js +1 -0
- package/web/dist/assets/typescript-DpDozqSz.js +1 -0
- package/web/dist/assets/typespec-BGhR2SqG.js +1 -0
- package/web/dist/assets/typst-GHzvrUad.js +1 -0
- package/web/dist/assets/v-DBj4K9d_.js +1 -0
- package/web/dist/assets/vala-zf12oZj6.js +1 -0
- package/web/dist/assets/vb-DOwzaHZ0.js +1 -0
- package/web/dist/assets/verilog-B2gwz-sJ.js +1 -0
- package/web/dist/assets/vesper-DdrHHSXu.js +1 -0
- package/web/dist/assets/vhdl-BroJfC0k.js +1 -0
- package/web/dist/assets/viml-DvXPmvsu.js +1 -0
- package/web/dist/assets/vitesse-black-fwtXNY1n.js +1 -0
- package/web/dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/web/dist/assets/vitesse-light-VbXTXTou.js +1 -0
- package/web/dist/assets/vue-CqkncxvB.js +1 -0
- package/web/dist/assets/vue-html-DrD64fqb.js +1 -0
- package/web/dist/assets/vue-vine-BJy37wv2.js +1 -0
- package/web/dist/assets/vyper-CgoNMtux.js +1 -0
- package/web/dist/assets/wasm-BnjxR4X6.js +1 -0
- package/web/dist/assets/wasm-ByWQv1Qj.js +1 -0
- package/web/dist/assets/wenyan-C8pVoKbM.js +1 -0
- package/web/dist/assets/wgsl-BsKzXJz4.js +1 -0
- package/web/dist/assets/wikitext-ClFFjSW2.js +1 -0
- package/web/dist/assets/wit-DdvCle-K.js +1 -0
- package/web/dist/assets/wolfram-DLL8P-h_.js +1 -0
- package/web/dist/assets/xml-CtXDvb3g.js +1 -0
- package/web/dist/assets/xsl-9J2XPEqv.js +1 -0
- package/web/dist/assets/yaml-CKsrpaXo.js +1 -0
- package/web/dist/assets/zenscript-BnlCZFoB.js +1 -0
- package/web/dist/assets/zig-CMLA9XwU.js +1 -0
- package/web/dist/favicon.svg +1 -0
- package/web/dist/index.html +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Armanc Keser
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,134 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Downstream
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Code review that reads a change the way it runs.
|
|
4
|
+
|
|
5
|
+
Ask your coding agent to review a pull request, or to explain part of the codebase, and Downstream opens next to it: the change laid out from its entry points, each call one click deeper, the agent's reasons pinned to the lines they're about, and a margin where you talk it through with the agent while it listens. When you agree, the review you arrived at goes to the pull request: your verdict, your words, inline comments the author can apply in one click.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Home: what needs you
|
|
10
|
+
|
|
11
|
+
`downstream home` opens a page with every pull request that needs you, for one GitHub account at a time, each with one next action: *Merge*, *Review*, *Re-review*, *Address 3 threads*, *Fix checks*, *Resolve conflicts*. One is **Now**. Pins come next in your order, then the rest ranked by what unblocks most. Pull requests waiting on someone else fold away.
|
|
12
|
+
|
|
13
|
+
- **Accounts.** Every account `gh auth status` knows, one at a time (`1`, `2`). Inboxes never merge.
|
|
14
|
+
- **Stacks.** A stack (`gh stack`) shows up as one row. Only the bottom layer can merge, so the row's action is the lowest layer that can move.
|
|
15
|
+
- **Press a pull request** and Claude Code starts on it inside Downstream (through the Agent SDK, with your Claude Code settings). A review opens as soon as the agent writes it; other moves happen in a session page where you talk to the agent. Reading runs without asking; anything that writes asks you in the browser, and sending the review is always yours. **Move to terminal** stops it here and copies `claude --resume <id>`.
|
|
16
|
+
|
|
17
|
+
Keys: `j`/`k` move, `↵` start, `n` Now, `p` pin, `r` refresh. In a review, one button is always the next thing to do (answer the agent, the next walkthrough step, send), and `↵` presses it.
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
A diff is a list of files, top to bottom, alphabetically. Nobody understands code that way. You find where control comes in, follow what it calls, and stop wherever something looks odd to ask why it's like that. The diff never tells you the entry point, never shows who calls the function you're reading, and never says why the author chose this interface over the obvious other one. That lives in the author's head, or now in the agent's context, and it's gone when the PR merges.
|
|
22
|
+
|
|
23
|
+
Downstream gives the change that shape:
|
|
24
|
+
|
|
25
|
+
- **Doors.** The entry points into the change: the route, the command, the component a page renders. Nothing else in the change calls them, so you start there.
|
|
26
|
+
- **Trail.** Open a door and its code appears scoped to just that symbol, numbered like the real file. Click a call and the callee opens below it; the frame above folds to the one line that made the call. You read down the stack the way the code runs.
|
|
27
|
+
- **Margin.** The agent writes `why` notes where a careful reader would stop, `decision` notes with the alternatives that lost, and findings with a severity and a failure scenario. You select lines and ask. It answers in the margin while you keep reading.
|
|
28
|
+
- **Map.** The whole change as a system, left to right from its doors: what's new, what's changed, which calls are new, and which existing code reaches in.
|
|
29
|
+
- **Send.** Findings are numbered (*fix 2 and 5*) and carry the problem, the smallest fix and what happens if you ship anyway. Each thread decides whether it reaches the author and in what words; the conversation stays private. Send review shows the exact review GitHub will get and posts it only when you confirm.
|
|
30
|
+
|
|
31
|
+
Hover anything in the code for its real TypeScript type and docs. Hover a symbol on the map or in a note for its purpose, callers, calls and open findings.
|
|
32
|
+
|
|
33
|
+
| | |
|
|
34
|
+
|---|---|
|
|
35
|
+
|  |  |
|
|
36
|
+
|  |  |
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
## How it works with an agent
|
|
41
|
+
|
|
42
|
+
Downstream is a [Claude Code skill](SKILL.md). The agent drives a small CLI; you use the browser. Both go through the same actions on the same review, so either side can do anything the other can.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
downstream open --pr 42 # analyze the change, open the browser
|
|
46
|
+
downstream show # the agent reads the map: doors, symbols, edges
|
|
47
|
+
downstream code pickNow # and the code, numbered, with +/- marks and callers
|
|
48
|
+
downstream apply plan.json # it writes the walkthrough: summary, steps, why, decisions, findings
|
|
49
|
+
downstream wait # then listens; returns when you ask, reply, resolve or finish
|
|
50
|
+
downstream reply 2 "…" # and answers in the margin
|
|
51
|
+
downstream go openFirst # or moves your view to what it's talking about
|
|
52
|
+
downstream outgoing 2 "…" # words a finding for the author (or --exclude keeps it private)
|
|
53
|
+
downstream draft # the review exactly as it would be sent; you press Send
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`downstream open --teach src/sync` does the same for existing code: no diff, a tour of how one request moves through that part of the system.
|
|
57
|
+
|
|
58
|
+
The analyzer does the mechanical part. It diffs the change against the merge base, finds every function, method, class, type, component and route that changed (JSDoc included), and resolves calls, renders and type uses through the TypeScript compiler. It finds unchanged callers, and works out which calls are new in the change and which disappeared. The agent does the part only a reader can: what each piece is for, the order to read it in, why it's shaped this way, and what's wrong with it.
|
|
59
|
+
|
|
60
|
+
## Built on agent-first primitives
|
|
61
|
+
|
|
62
|
+
Downstream follows the [agent-first-apps](https://github.com/armanckeser/agent-first-apps) contract, adapted to a tool that starts in a second inside any repository:
|
|
63
|
+
|
|
64
|
+
- **One source of truth.** A SQLite database per repository (`.downstream/review.db`, kept out of git), and one for the home page (`~/.downstream/home.db`: Now, pins, which repository and agent session belong to which pull request). The agent can query it read-only with `downstream sql`, and that connection cannot write.
|
|
65
|
+
- **One action surface.** Every change is an action defined once in `server/actions.ts` with a schema: `note.add`, `note.reply`, `steps.set`, `symbol.annotate`, `navigate`, …. The browser, the CLI and plain HTTP all call it. `downstream actions` lists them.
|
|
66
|
+
- **Live sync.** Every action emits an event. The browser follows them over server-sent events and updates as the agent writes.
|
|
67
|
+
- **Shared application state.** The browser reports what you're looking at (the view, the trail of open frames, your selected lines), so when you ask about "this" the agent already knows what this is (`downstream screen`). The agent can move your view with `navigate`.
|
|
68
|
+
- **Presence.** While `downstream wait` is blocked on you, the UI shows the agent as listening.
|
|
69
|
+
|
|
70
|
+
Postgres and Electric would be the agent-first default. A review is short-lived, local and per repository, though, so an embedded database and SSE keep the same guarantees with nothing to start.
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
Run the home page straight from npm, from any directory:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @armanckeser/downstream home
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To let Claude Code drive reviews, install it as a skill instead:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/armanckeser/downstream ~/.claude/skills/downstream
|
|
84
|
+
cd ~/.claude/skills/downstream && npm install && npm run build
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then start the home page from any directory; it doesn't need to be inside a repository:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
node ~/.claude/skills/downstream/bin/downstream.mjs home
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
One server runs per machine (`~/.downstream/`). Press a pull request and Downstream finds your clone of that repository under `~/projects`, `~/code`, `~/src`, `~/dev` or `~` (set `DOWNSTREAM_REPO_ROOTS` to search elsewhere), and makes a partial clone in `~/.downstream/repos/` when you don't have one.
|
|
94
|
+
|
|
95
|
+
Or ask Claude Code to review something: *"help me review this branch"*, *"review PR 42 with me"*, *"teach me how the sync engine works"*. The first `open` in a repository takes a few seconds while TypeScript loads the project; after that the hovers are instant.
|
|
96
|
+
|
|
97
|
+
Requires Node 22.18 or newer and git. `--pr` uses the GitHub CLI.
|
|
98
|
+
|
|
99
|
+
**Languages.** TypeScript and JavaScript go through the compiler: symbols, call resolution, callers, real types on hover. Python is read from indentation and imports: functions, classes and methods with decorators and docstrings, FastAPI/Flask routes as doors, calls through imports and `self`, callers across the repo, signatures and docstrings on hover. Tests in either are marked as tests. Other languages arrive as one symbol per changed file; the agent splits them into real functions and draws the edges (`addSymbols`, `edges` in `apply`).
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install
|
|
105
|
+
npm run build # the web UI, served by the local server
|
|
106
|
+
npm test # analyzer tests against throwaway git repos
|
|
107
|
+
npm run typecheck
|
|
108
|
+
DOWNSTREAM_API=http://127.0.0.1:4317 npm run dev:web # UI with hot reload against a running review
|
|
109
|
+
npm pack # what npm would publish: the UI and dist/ (the CLI and server compiled to JS)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
A clone runs the TypeScript sources with Node's type stripping. A published copy lives under `node_modules`, where Node won't strip types, so `npm pack` and `npm publish` compile `cli/`, `server/` and `domain/` into `dist/` first (`server/install.ts` picks which one runs).
|
|
113
|
+
|
|
114
|
+
| Path | What lives there |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `SKILL.md` | The agent's instructions: the loop, what a good walkthrough contains, how to talk in the margin |
|
|
117
|
+
| `domain/model.ts` | The review model, shared by server, CLI and browser |
|
|
118
|
+
| `server/analyze/` | Git sides, TypeScript declarations, the flow graph |
|
|
119
|
+
| `server/actions.ts` | Every action, defined once |
|
|
120
|
+
| `server/publish.ts` | The review as the PR author sees it, and sending it |
|
|
121
|
+
| `server/analyze/py.ts` | Python declarations, imports and calls |
|
|
122
|
+
| `server/main.ts` | HTTP, SSE, `wait`; one server for every repository (`/r/<repo>/api`) |
|
|
123
|
+
| `domain/inbox.ts` | The next action for each pull request, stacks, Now and pins |
|
|
124
|
+
| `domain/next-move.ts` | The one next thing to do inside a review |
|
|
125
|
+
| `domain/approval.ts` | Which agent tool calls run without asking |
|
|
126
|
+
| `server/github.ts`, `server/gh.ts` | The inbox from GitHub search, per account |
|
|
127
|
+
| `server/agent-host.ts`, `server/home.ts` | Agent sessions started from the home page; the home API |
|
|
128
|
+
| `cli/downstream.ts` | The agent's CLI |
|
|
129
|
+
| `web/src/` | The review surface (React, Tailwind, [Pierre diffs](https://diffs.com) and [trees](https://www.npmjs.com/package/@pierre/trees), dagre) |
|
|
130
|
+
| `scripts/demo.mjs` | Records the demo above against a real repository |
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
[MIT](LICENSE)
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: downstream
|
|
3
|
+
description: Open a shared, live review surface for a code change or an area of code, walk the user through it, discuss it with them there, and (for pull requests) send the review you arrive at together. Use when the user asks to review a PR, branch or diff ("help me review this", "review PR 42", "walk me through this change"), or to explain how existing code works ("teach me how auth works", "give me a tour of src/sync"). TypeScript, JavaScript and Python are analyzed; other languages are mapped by hand.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Downstream
|
|
7
|
+
|
|
8
|
+
Downstream turns a change into something a person can read the way the code runs: start at an entry point ("a door"), follow each call downstream, and see why each piece is the way it is. You (the agent) map the change, write the walkthrough, and review it; the user reads it in a browser and talks to you in the margin; together you arrive at a review, and when the user says so, it goes to the pull request. You both read and write the same review through the same actions, and you hear the user through `downstream wait`.
|
|
9
|
+
|
|
10
|
+
Run the CLI as `node "${CLAUDE_SKILL_DIR}/bin/downstream.mjs" <command>`. Below it is written `downstream <command>`. Run review commands from inside the repository being reviewed; `home`, `url` and `stop` work from anywhere. The first run installs dependencies and builds the UI (about a minute).
|
|
11
|
+
|
|
12
|
+
## The loop
|
|
13
|
+
|
|
14
|
+
1. **Open** the review.
|
|
15
|
+
```bash
|
|
16
|
+
downstream open --pr 42 # someone's pull request (uses gh; no checkout needed)
|
|
17
|
+
downstream open # working tree (incl. uncommitted) vs main
|
|
18
|
+
downstream open --base main --head feature-x
|
|
19
|
+
downstream open --teach src/sync # explain existing code instead of a diff
|
|
20
|
+
```
|
|
21
|
+
It prints the URL and opens the browser. Tell the user the URL in one line, then keep working; the UI fills in live as you write. When Downstream started you (see *Running inside Downstream*), the user is already watching and the browser moves to the review by itself.
|
|
22
|
+
|
|
23
|
+
2. **Understand** before you judge. `downstream show` prints the map: doors, every symbol with its status, flow edges (`*` = new in this change, `x` = removed), tests, threads. `downstream code <symbol>` prints a symbol's code with real line numbers and `+`/`-` marks, plus who calls it and what it calls. Then read past the map: the PR description, the callers of every changed function, the tests, where data comes in, what is stored, what goes out. A change can break code it doesn't touch: when a signature, return value or behavior changes, check every caller. Find the expected load (README, deploy config) and judge scale against it.
|
|
24
|
+
|
|
25
|
+
3. **Write** the walkthrough and your review in one batch: a JSON file and `downstream apply plan.json` (shape below). You can apply again; steps are replaced as a whole, notes are added, `updates` edit existing threads.
|
|
26
|
+
|
|
27
|
+
4. **Converse.** Run `downstream wait`. It blocks until the user replies, asks, flags, edits, resolves or ends the session, then prints what happened with the exact command to answer (and their selected code, when they asked about a selection). While it blocks, the UI shows you as *listening*. Answer, then `wait` again.
|
|
28
|
+
|
|
29
|
+
5. **Arrive at the review.** When the conversation settles, draft what the author will read (see *Sending the review*). The user checks it in **Send review** and sends it, or tells you to.
|
|
30
|
+
|
|
31
|
+
## What to look for
|
|
32
|
+
|
|
33
|
+
Review like the senior developer who gets paged when it breaks. In order: correct, safe, holds under the expected load, tested, fast, lean.
|
|
34
|
+
|
|
35
|
+
- **bug**: wrong result, crash, missed edge case (empty, zero, last item, rounding, time zones), a caller broken by the change, a fix in one caller while the shared function stays broken.
|
|
36
|
+
- **risk**: security (injection, secrets, unchecked user input), data loss (swallowed errors, writes in the wrong order, no transaction), migrations that change what existing rows mean.
|
|
37
|
+
- **scale**: fine for one user, wrong for many: check-then-write races, a query per item, unbounded growth, per-process state that must be shared.
|
|
38
|
+
- **test**: risky new logic (a branch, a parser, money, security, data writes, a bug fix) with no test that fails when it breaks. One good test, not coverage.
|
|
39
|
+
- **speed**: big slowdowns are findings; small wins are nice-to-haves.
|
|
40
|
+
- **lean**: code that shouldn't exist (dead, speculative, an abstraction with one implementation), a helper the repo already has (name the path), a dependency for a few lines, near-copies that must change together.
|
|
41
|
+
|
|
42
|
+
Every finding needs a concrete case: *this input or situation leads to this wrong result*. No case, no finding. Re-read the lines before you report: confirm the caller exists, the value can really be empty, the code really is unused. Propose the smallest fix that works; prefer fixes that delete code. No style taste, no "consider", no vague worries. Three real findings beat ten weak ones.
|
|
43
|
+
|
|
44
|
+
## What a good walkthrough contains
|
|
45
|
+
|
|
46
|
+
Write for a reader who has not seen the code: short sentences, everyday words, explain a term the first time. Name things; never say "it" when you can say `openFirst`. Wrap symbol names in backticks: in the UI they become links that show the code on hover and open it on click.
|
|
47
|
+
|
|
48
|
+
- **summary** (3 to 8 sentences or a short list): what this change does, in behavior, then where the risk is. Not a file list.
|
|
49
|
+
- **symbols**: a one-line purpose (`summary`) for every changed symbol and any unchanged one that matters to the flow ("what is this for?"). Fix `entry` flags: a door is where control enters the change (a route, a job, a CLI command, an exported function called from unchanged code, a component a page renders). Unmark what the analyzer guessed wrong.
|
|
50
|
+
- **steps** (usually 3 to 8): the order to read the change, door first, then downstream along real calls. Each step anchors to a symbol (`symbolId`) and usually `lines`; its `body` says what happens at this point in the flow and why it's built this way. Point at the line that matters; don't repeat the code.
|
|
51
|
+
- **notes**, each anchored to a symbol and, when about specific lines, `lines` (new-side numbers; `side: "old"` for removed code). Every note gets a number (`#1`, `#2`, …) the user can refer to; commands accept `3` or `#3` wherever they take a thread.
|
|
52
|
+
- `finding`: `severity` is `blocker` (shown as **must fix**: bug, security, data loss, breaks at the expected load), `concern` (**should fix**: risky code without a test, real slowness, duplication, code that shouldn't exist) or `nit` (**nice to have**). Set `category` (bug, risk, scale, test, speed, lean). `title` names the problem; `body` is the problem with its concrete case; `fix` is the smallest fix; `impact` says what happens if we ship it anyway; `suggestion` (`{lines, code}`) when the fix is concrete. A suggestion whose lines match the note's lines becomes a GitHub suggestion the author can apply in one click.
|
|
53
|
+
- `decision`: an abstraction or interface choice, with the `alternatives` that lost and their tradeoffs. For every new type, function boundary, dependency or data shape ask: why this shape and not the obvious other one? Is this the right layer? Anchor it to the shape's symbol; the overview shows each shape with its decision.
|
|
54
|
+
- `why`: the reason behind anything that would make a careful reader stop.
|
|
55
|
+
- `question`: you need the author's (or user's) intent to judge something.
|
|
56
|
+
- **edges**: relationships the analyzer can't see (events, queues, HTTP between services, dynamic dispatch, DI wiring), with a `label`. `removeEdges` for noise.
|
|
57
|
+
- **addSymbols**: for other languages (they arrive as one `module` symbol per file) add the real functions with `file`, `name`, `lines`, `status`, then connect them with `edges`. In TS/JS/Python you can pass just `line` and the declaration around it is found.
|
|
58
|
+
|
|
59
|
+
Set `"status": "ready"` when the walkthrough is complete.
|
|
60
|
+
|
|
61
|
+
### plan.json
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"summary": "Price tracking stops being something you switch on…\n\nThe risk is in the migration: …",
|
|
66
|
+
"status": "ready",
|
|
67
|
+
"symbols": [
|
|
68
|
+
{ "id": "backend/app/features/price_tracking/service.py#apply_price_observation", "summary": "Records a fetched price; now also turns tracking on." },
|
|
69
|
+
{ "id": "backend/app/features/wishlist_item/models.py#WishlistItem", "entry": false }
|
|
70
|
+
],
|
|
71
|
+
"steps": [
|
|
72
|
+
{ "title": "Tracking turns on when a check succeeds", "symbolId": "backend/app/features/price_tracking/service.py#apply_price_observation",
|
|
73
|
+
"lines": { "start": 267, "end": 271 }, "body": "A price we could read proves the page is trackable, so…" }
|
|
74
|
+
],
|
|
75
|
+
"notes": [
|
|
76
|
+
{ "kind": "finding", "severity": "blocker", "category": "risk", "symbolId": "backend/app/alembic/versions/f1a2_add_flag.py#upgrade",
|
|
77
|
+
"lines": { "start": 19, "end": 27 }, "title": "Everyone who turned tracking off is opted back in",
|
|
78
|
+
"body": "The new column defaults to false for every existing row, so an item whose owner switched tracking off…",
|
|
79
|
+
"fix": "Backfill in upgrade(): items that had a verified price and are off without a pause were turned off by their owner.",
|
|
80
|
+
"impact": "Owners who opted out start getting price notifications again the day this deploys.",
|
|
81
|
+
"suggestion": { "lines": { "start": 19, "end": 27 }, "code": " op.add_column(…)\n op.execute(\"UPDATE …\")" } },
|
|
82
|
+
{ "kind": "decision", "symbolId": "backend/app/background/price_check.py#check_tracked_prices", "title": "Group by store instead of a global rate limiter",
|
|
83
|
+
"body": "…", "alternatives": [ { "option": "One global queue with a smaller delay", "tradeoff": "Every site sees faster requests." } ] },
|
|
84
|
+
{ "kind": "question", "symbolId": "backend/app/background/price_check.py#check_tracked_prices", "title": "Is one scheduler process the expected deployment?", "body": "…" }
|
|
85
|
+
],
|
|
86
|
+
"updates": [ { "noteId": "2", "outgoing": { "include": true, "body": "…" } } ],
|
|
87
|
+
"outgoingBody": "…",
|
|
88
|
+
"edges": [ { "from": "src/server/jobs.ts#enqueue", "to": "src/worker/run.ts#handle", "kind": "calls", "label": "via the jobs queue" } ]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Two full examples from real reviews live in `examples/` (`kumbara-equity-grants.json` for TypeScript, `wishlist-auto-tracking.json` for Python).
|
|
93
|
+
|
|
94
|
+
## Talking in the margin
|
|
95
|
+
|
|
96
|
+
| The user… | You run |
|
|
97
|
+
|---|---|
|
|
98
|
+
| asks a question (`wait` shows `The user asked [n_x]…` with their selected code) | `downstream reply n_x "…"` |
|
|
99
|
+
| replies in a thread | `downstream reply 3 "…"`, or `downstream resolve 3 "why"` when it's settled |
|
|
100
|
+
| pushes back on a finding or decision | answer honestly. If they're right, say so and `resolve` or `dismiss` it with the reason; if the finding changes, `downstream do note.update '{"noteId":"3","body":"…"}'` |
|
|
101
|
+
| says "this", "here" | `downstream screen` shows their view, open frames and selected lines |
|
|
102
|
+
| says "fix 2 and 5" (their own code) | edit the code, `downstream reanalyze`, then `downstream resolve 2 "Fixed: …"` |
|
|
103
|
+
| edits a thread's outgoing wording | `wait` reports it; leave their wording alone unless asked |
|
|
104
|
+
| gives a verdict, sends, or ends the session | acknowledge in chat |
|
|
105
|
+
|
|
106
|
+
- `downstream go <symbol> [--lines a-b]`, `go step 3`, `go thread 3`, `go map` moves the user's view. Use it when your answer is "look at this".
|
|
107
|
+
- `downstream note <kind> <symbol> --title "…" [--body] [--severity] [--category] [--fix] [--impact] [--lines a-b]` adds one thread without a full apply.
|
|
108
|
+
- `downstream ask "…" --symbol s` asks the user something.
|
|
109
|
+
- `downstream sql "select * from v_notes where status = 'open'"` reads anything (read-only connection; views `v_symbols`, `v_edges`, `v_notes`, `v_replies`).
|
|
110
|
+
- `downstream actions` lists every action with its input schema; `downstream do <action> '<json>'` calls one. The browser uses the same actions, so anything the user can do, you can do.
|
|
111
|
+
|
|
112
|
+
Replies are markdown, shown in your voice in the margin. Keep them short: the claim, the evidence (a line, a caller), what you'd do.
|
|
113
|
+
|
|
114
|
+
## Sending the review
|
|
115
|
+
|
|
116
|
+
The conversation in the margin is between you and the user and never leaves. What the PR author reads is a separate, explicit draft:
|
|
117
|
+
|
|
118
|
+
- **Which threads go out.** The user answers that per thread in the margin: **Send to author** (settles it, and it goes out) or **Don't send** for findings, **Got it** (private) or **Send to author too** for your explanations. That choice is `outgoing.include`; never override it. Threads the user hasn't answered: open findings go out, everything else stays private. You change it only when asked: `downstream outgoing 3 "…"` (send, in these words) or `downstream outgoing 4 --exclude`.
|
|
119
|
+
- **The words.** Without outgoing wording, a finding goes out as written (severity, title, problem, fix, suggestion, impact). Reword when the conversation changed the finding, or when it should read as a question to the author. Write to the author, not to the user: no references to your conversation, and no `#3` thread numbers (on GitHub those link to issues and pull requests); name the problem instead.
|
|
120
|
+
- **The body.** `outgoingBody` (via `apply` or `downstream do review.update '{"outgoingBody":"…"}'`): what the change does in a sentence or two, what must change before merging, and anything you could not check. Plain and kind.
|
|
121
|
+
- **The verdict.** `downstream verdict changes|approve|comment "…"`. GitHub doesn't allow approving or requesting changes on your own pull request; use comment there.
|
|
122
|
+
- `downstream draft` prints exactly what would be sent: the event, the body, each inline comment, and the comments that fall outside the diff (GitHub only takes inline comments on changed lines and their context, so those go into the body).
|
|
123
|
+
|
|
124
|
+
Sending is public and permanent. **Never run `downstream send` on your own initiative.** The user sends from the Send review panel, or tells you to in chat ("send it"); then run `downstream draft`, show them the verdict and the number of comments in one line, and send only after they confirm. Only pull request reviews (`open --pr`) can be sent; for anything else the panel copies the review as markdown. If the PR changed since you opened it, `send` refuses: tell the user, and offer to re-open it (`open --pr <n>`) or, only if they say so, `send --force`.
|
|
125
|
+
|
|
126
|
+
## Languages
|
|
127
|
+
|
|
128
|
+
- **TypeScript / JavaScript**: the compiler resolves calls, renders, type uses and callers; hover shows real types and docs. Inner functions of large containers (Effect service layers, app builders) are lifted into their own symbols, and a service's `return { createGrant }` is followed back to the function.
|
|
129
|
+
- **Python**: declarations come from indentation (decorators and docstrings included); FastAPI/Flask/Celery-style decorators mark routes and jobs as doors. Calls are resolved through each file's imports, `self.`/`cls.`, fresh instances (`Service().create()`), and unique method names; callers are found across the repo; hover shows the signature and docstring. There is no type checker: when a call goes through an injected object the analyzer can't see, add the edge yourself.
|
|
130
|
+
- **Tests** are marked as tests: never doors, shown on a frame as *tested by*, hidden on the map unless toggled.
|
|
131
|
+
|
|
132
|
+
## Teaching mode
|
|
133
|
+
|
|
134
|
+
`downstream open --teach <paths…>` maps existing code instead of a diff: every declaration is a symbol, edges are the calls between them, doors are exported functions and routes nothing else in the set calls. The job is the same minus findings unless asked: what this part of the system is for, purposes for the important symbols, a walkthrough that follows one real request or operation end to end, and `why`/`decision` notes on the shapes a newcomer would question. Large maps open scoped to your walkthrough.
|
|
135
|
+
|
|
136
|
+
## Running inside Downstream
|
|
137
|
+
|
|
138
|
+
The user can start you from Downstream's home page (`downstream home`) by pressing a pull request. Then `DOWNSTREAM_HOSTED=1` is set and:
|
|
139
|
+
|
|
140
|
+
- The user reads your output and answers your permission requests in the browser, not a terminal. Talk to them in the margin, and keep `downstream wait` running between turns; a message they write while you're idle also wakes you.
|
|
141
|
+
- Reading never asks: file tools, searches, and read-only shell (`git log/show/diff`, `gh pr view`, `rg`, loops over those) run straight away. Anything that writes asks, and so does an expanded argument (`$f`, `$(…)`) to `git`, `gh` or the CLI, so prefer literal arguments. `downstream send` always asks.
|
|
142
|
+
- For work other than a review (failing checks, feedback to address, conflicts), make changes in a git worktree and say what you'll change before you change it.
|
|
143
|
+
- The user can move the session to their terminal (`claude --resume`); nothing changes for you.
|
|
144
|
+
|
|
145
|
+
## Housekeeping
|
|
146
|
+
|
|
147
|
+
- One server per machine (`~/.downstream/`, or `$DOWNSTREAM_HOME`); each repository's reviews live in `<repo>/.downstream/` (SQLite), added to `.git/info/exclude`. `downstream status`, `downstream url`, `downstream stop`, `downstream home`. An updated skill restarts the server and rebuilds the UI on the next `open`.
|
|
148
|
+
- `downstream reanalyze` re-reads the code after edits and keeps summaries, threads and the walkthrough.
|
|
149
|
+
- Reopening (`downstream open`) starts a fresh review and makes it current; old ones stay in the database.
|
|
150
|
+
- If `open` fails, `~/.downstream/server.log` has the server's output.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs the CLI from the package's own directory so it works from any repository.
|
|
3
|
+
// A clone runs the TypeScript sources with Node's built-in type stripping. A
|
|
4
|
+
// published copy (npx, npm install) sits under node_modules, where Node refuses
|
|
5
|
+
// to strip types, so it runs the JavaScript compiled into dist/ at publish time.
|
|
6
|
+
import { spawnSync, execFileSync } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const skillDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const published = skillDir.split(path.sep).includes("node_modules");
|
|
13
|
+
const entry = published ? path.join(skillDir, "dist", "cli", "downstream.js") : path.join(skillDir, "cli", "downstream.ts");
|
|
14
|
+
if (!published && !existsSync(path.join(skillDir, "node_modules", "typescript"))) {
|
|
15
|
+
console.error("downstream: installing dependencies (first run)…");
|
|
16
|
+
execFileSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: skillDir, stdio: "inherit", shell: process.platform === "win32" });
|
|
17
|
+
}
|
|
18
|
+
const res = spawnSync(process.execPath, ["--no-warnings=ExperimentalWarning", entry, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
19
|
+
process.exit(res.status ?? 1);
|