@repo-os/repoos 0.5.57 → 0.5.59
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/README.md +9 -1
- package/dist/.build-info.json +2 -2
- package/dist/.build-stamp.json +1 -1
- package/dist/cli/index.js +20 -19
- package/dist/commands/check.js +5 -0
- package/dist/commands/init.d.ts +7 -1
- package/dist/commands/init.js +147 -13
- package/dist/commands/serve-banner.test.d.ts +1 -0
- package/dist/commands/serve-banner.test.js +26 -0
- package/dist/commands/serve.d.ts +12 -0
- package/dist/commands/serve.js +72 -21
- package/dist/commands/shot.d.ts +31 -0
- package/dist/commands/shot.js +427 -0
- package/dist/commands/tasks.js +2 -2
- package/dist/core/agent-compatibility.json +20 -0
- package/dist/core/agent-contract.js +67 -11
- package/dist/core/agent-updates.js +3 -0
- package/dist/core/areas.d.ts +102 -0
- package/dist/core/areas.js +145 -0
- package/dist/core/canary-repo.d.ts +12 -0
- package/dist/core/canary-repo.js +111 -0
- package/dist/core/canary-repo.test.d.ts +1 -0
- package/dist/core/canary-repo.test.js +71 -0
- package/dist/core/canary.d.ts +21 -7
- package/dist/core/canary.js +80 -12
- package/dist/core/canary.test.d.ts +1 -0
- package/dist/core/canary.test.js +73 -0
- package/dist/core/check-failure-summary.d.ts +11 -0
- package/dist/core/check-failure-summary.js +29 -0
- package/dist/core/check-store.d.ts +5 -0
- package/dist/core/check-store.js +29 -14
- package/dist/core/config.d.ts +13 -2
- package/dist/core/config.js +90 -0
- package/dist/core/context-pack.js +9 -4
- package/dist/core/detect.js +13 -0
- package/dist/core/doctor.js +106 -0
- package/dist/core/git-activity.d.ts +35 -0
- package/dist/core/git-activity.js +26 -0
- package/dist/core/git.d.ts +53 -6
- package/dist/core/git.js +135 -27
- package/dist/core/models.js +27 -1
- package/dist/core/repoos.d.ts +6 -1
- package/dist/core/repoos.js +20 -1
- package/dist/core/shot-targets.d.ts +90 -0
- package/dist/core/shot-targets.js +191 -0
- package/dist/core/tailscale.d.ts +15 -0
- package/dist/core/tailscale.js +76 -1
- package/dist/core/task.js +35 -2
- package/dist/core/types.d.ts +45 -1
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.js +16 -0
- package/dist/server/agents.d.ts +69 -3
- package/dist/server/agents.js +280 -9
- package/dist/server/area-migration.d.ts +24 -0
- package/dist/server/area-migration.js +131 -0
- package/dist/server/freeform.d.ts +4 -1
- package/dist/server/freeform.js +52 -33
- package/dist/server/handoff.js +5 -0
- package/dist/server/integration-orchestrator.js +4 -0
- package/dist/server/live-index.d.ts +11 -0
- package/dist/server/live-index.js +1 -0
- package/dist/server/local-token.d.ts +10 -0
- package/dist/server/local-token.js +62 -0
- package/dist/server/preview.js +30 -10
- package/dist/server/repo-status.d.ts +95 -0
- package/dist/server/repo-status.js +313 -0
- package/dist/server/review.js +1 -1
- package/dist/server/routes/config.js +91 -1
- package/dist/server/routes/info.js +4 -2
- package/dist/server/routes/repo-log.d.ts +11 -0
- package/dist/server/routes/repo-log.js +15 -0
- package/dist/server/routes/tasks.d.ts +3 -0
- package/dist/server/routes/tasks.js +67 -3
- package/dist/server/server.js +119 -30
- package/dist/server/shot-context.d.ts +21 -0
- package/dist/server/shot-context.js +30 -0
- package/dist/server/shots.d.ts +42 -0
- package/dist/server/shots.js +170 -0
- package/dist/server/watcher.d.ts +38 -0
- package/dist/server/watcher.js +109 -2
- package/dist/server/write.d.ts +6 -1
- package/dist/server/write.js +20 -2
- package/dist/ui/assets/{AgentModelModal-Ced4h4sp.js → AgentModelModal-DzP177gt.js} +1 -1
- package/dist/ui/assets/{AgentsView-fJc8ZQRN.js → AgentsView-CA6vNzB_.js} +1 -1
- package/dist/ui/assets/{ChecksView-DK2wcClf.css → ChecksView-DXaRYvbW.css} +1 -1
- package/dist/ui/assets/ChecksView-k-3IMvCn.js +0 -0
- package/dist/ui/assets/{ContextView-CTGAeIvz.css → ContextView-CE7UCqYM.css} +1 -1
- package/dist/ui/assets/ContextView-DIn7440o.js +4 -0
- package/dist/ui/assets/{DashboardView-BlSDOM4G.js → DashboardView-Dycyb_k9.js} +1 -1
- package/dist/ui/assets/{DiffView-nH7u0JwJ.js → DiffView-DMGq4a2J.js} +1 -1
- package/dist/ui/assets/{InputsView-BZ3T5jsY.js → InputsView-DLdABoFS.js} +1 -1
- package/dist/ui/assets/{LoginView-CX6blOC4.js → LoginView-DJRB0cU8.js} +1 -1
- package/dist/ui/assets/ReleasesView-CURY5err.css +1 -0
- package/dist/ui/assets/{ReleasesView-C5PaQX6n.js → ReleasesView-UavG0AWn.js} +2 -2
- package/dist/ui/assets/SettingsView-B0CnWYfh.js +4 -0
- package/dist/ui/assets/StoriesView-D1Rm_igY.js +1 -0
- package/dist/ui/assets/{VoiceDictate-CifZUkzt.js → VoiceDictate-CCJfAGJk.js} +1 -1
- package/dist/ui/assets/WorkView-DoN6rK5X.js +2 -0
- package/dist/ui/assets/{abnfDiagram-VCTEODGH-CxKkcu03.js → abnfDiagram-VCTEODGH-DNJeaBue.js} +1 -1
- package/dist/ui/assets/architecture-7GRP2DOG-CE8k1klh.js +1 -0
- package/dist/ui/assets/{architectureDiagram-5GKGNRK7-B7yFyWIW.js → architectureDiagram-5GKGNRK7-CFYttSDP.js} +1 -1
- package/dist/ui/assets/{blockDiagram-I7D4REHJ-Dqn5piZv.js → blockDiagram-I7D4REHJ-D4x6ObYp.js} +1 -1
- package/dist/ui/assets/{c4Diagram-7LVT6UL2-BDourwGX.js → c4Diagram-7LVT6UL2-CxtP3-uN.js} +1 -1
- package/dist/ui/assets/channel-YsXE3acZ.js +1 -0
- package/dist/ui/assets/{chunk-4HAMMTFA-BGxQ0vmo.js → chunk-4HAMMTFA-CuuPSpo5.js} +1 -1
- package/dist/ui/assets/{chunk-75Z2AOVW-CoD1HiQQ.js → chunk-75Z2AOVW-DA5zPl9A.js} +1 -1
- package/dist/ui/assets/{chunk-DU6HZSFF-WQx_PGDE.js → chunk-DU6HZSFF-_OVfIMg9.js} +1 -1
- package/dist/ui/assets/{chunk-F27PBJKO-BmtJFWYx.js → chunk-F27PBJKO-DmAfAXfg.js} +1 -1
- package/dist/ui/assets/{chunk-GMAD6QVW-kRmn1tqd.js → chunk-GMAD6QVW-7rYiJcfH.js} +1 -1
- package/dist/ui/assets/{chunk-GVQU2GXP-C2SMD4HM.js → chunk-GVQU2GXP-DUvM8o5c.js} +1 -1
- package/dist/ui/assets/{chunk-IMKFNOWR-DRm68yqu.js → chunk-IMKFNOWR-Dug5EN3L.js} +1 -1
- package/dist/ui/assets/{chunk-L3NEJ4N5-Bdd0UO7i.js → chunk-L3NEJ4N5-B__JCALV.js} +1 -1
- package/dist/ui/assets/{chunk-OSK3NFVY-DzFP_5D1.js → chunk-OSK3NFVY-DNSy5va-.js} +1 -1
- package/dist/ui/assets/{chunk-P2QGCYS3-CxTbTe1z.js → chunk-P2QGCYS3-jZiRM4I9.js} +1 -1
- package/dist/ui/assets/{chunk-POPQ4Y6H-1sMFzZlT.js → chunk-POPQ4Y6H-DUE_WVHL.js} +1 -1
- package/dist/ui/assets/{chunk-PWAF6VOD-qwo7mGDG.js → chunk-PWAF6VOD-CbMevpLH.js} +1 -1
- package/dist/ui/assets/{chunk-SHT3W25Y-t7arLGqg.js → chunk-SHT3W25Y-MuvL_buO.js} +1 -1
- package/dist/ui/assets/{chunk-SVP7TREG-CDGoMrDa.js → chunk-SVP7TREG-lOHx_pKr.js} +1 -1
- package/dist/ui/assets/{chunk-TICWLB2K-DdCRvOgF.js → chunk-TICWLB2K-11z-MDx9.js} +1 -1
- package/dist/ui/assets/classDiagram-ZZMXUADV-DpeLlC2e.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-DpeLlC2e.js +1 -0
- package/dist/ui/assets/config-j80hL8dN.js +1 -0
- package/dist/ui/assets/{cynefin-OW5HDTMX-Dphr1hkJ.js → cynefin-OW5HDTMX-D8Tbtioe.js} +1 -1
- package/dist/ui/assets/{cynefinDiagram-5FMLGOSQ-BmNfqRyY.js → cynefinDiagram-5FMLGOSQ-DykcYZa1.js} +1 -1
- package/dist/ui/assets/{dagre-GXQ25YYZ-0__5BzkZ.js → dagre-GXQ25YYZ-BCjGpohz.js} +1 -1
- package/dist/ui/assets/{diagram-S7CK7UJ4-BCSWodGD.js → diagram-S7CK7UJ4-DrO95VpH.js} +1 -1
- package/dist/ui/assets/{diagram-UQ7AKVKN-DNY_hqmH.js → diagram-UQ7AKVKN-Bl2XIcjq.js} +1 -1
- package/dist/ui/assets/{diagram-VSXAHHWV-DgEipWnL.js → diagram-VSXAHHWV-D88EBlA1.js} +1 -1
- package/dist/ui/assets/{diagram-VX7I27RA-CXqwFFDY.js → diagram-VX7I27RA-Dd9fRIfD.js} +1 -1
- package/dist/ui/assets/{diagram-Z3DM3KII-DqGMJic1.js → diagram-Z3DM3KII-CDvHYGee.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-PWID7BFC-O7OLADLR.js → ebnfDiagram-PWID7BFC-ROLkMsCj.js} +1 -1
- package/dist/ui/assets/{erDiagram-RLTQ6QDP-BLybCuiJ.js → erDiagram-RLTQ6QDP-hgEdVaDk.js} +1 -1
- package/dist/ui/assets/eventmodeling-NTZA5JFV-D4JrdfJN.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-BuBvXaQp.js +1 -0
- package/dist/ui/assets/{ganttDiagram-EL5Y4UJY-PP7LUoJk.js → ganttDiagram-EL5Y4UJY-DyNYXsDm.js} +1 -1
- package/dist/ui/assets/{gitGraph-4MIJSDKK-jLh6GWq3.js → gitGraph-4MIJSDKK-DrwHooXn.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-WWUBYQGX-BpQqVPAf.js → gitGraphDiagram-WWUBYQGX-_DOOuskB.js} +1 -1
- package/dist/ui/assets/index-BqREmSY8.js +46 -0
- package/dist/ui/assets/index-RS-D6B89.css +1 -0
- package/dist/ui/assets/{info-A6RAGUB7-MKojffEb.js → info-A6RAGUB7-Bp4WG9KV.js} +1 -1
- package/dist/ui/assets/{infoDiagram-27XIBGKW-CDsY5bPn.js → infoDiagram-27XIBGKW-Bp9BY3BX.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-5VMMS53U-DlkSFpW_.js → ishikawaDiagram-5VMMS53U-Dmxsu_I6.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-3NMN7TZE-BQerIcFQ.js → journeyDiagram-3NMN7TZE-DyCFShcT.js} +1 -1
- package/dist/ui/assets/{kanban-definition-UXKFOSKX-DcOUAWHk.js → kanban-definition-UXKFOSKX-BY6mL0nV.js} +1 -1
- package/dist/ui/assets/{line-CWe6TPQq.js → line-CFvZJFGP.js} +1 -1
- package/dist/ui/assets/{mermaid-parser.core-B6lGkbHf.js → mermaid-parser.core-BlWQycY8.js} +3 -3
- package/dist/ui/assets/{mermaid.core-usGEYfph.js → mermaid.core-kuwSwalt.js} +4 -4
- package/dist/ui/assets/{mindmap-definition-YA3MSWOX-CRr1hJ6k.js → mindmap-definition-YA3MSWOX-Da8T6fiA.js} +1 -1
- package/dist/ui/assets/{packet-AYTQ26CC-Vrb5eklN.js → packet-AYTQ26CC-uA_F87fL.js} +1 -1
- package/dist/ui/assets/{pegDiagram-XKGWAZYB-DWgbaT0F.js → pegDiagram-XKGWAZYB-DlT4Tua4.js} +1 -1
- package/dist/ui/assets/{pie-WAS4IAKB-EBSi28Ou.js → pie-WAS4IAKB-CMlL534n.js} +1 -1
- package/dist/ui/assets/{pieDiagram-E7YTZNPT-CY7kTdf6.js → pieDiagram-E7YTZNPT-Dc1lWF3q.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-AXDQQJYC-DapXMfW1.js → quadrantDiagram-AXDQQJYC-BqVcYO8d.js} +1 -1
- package/dist/ui/assets/{radar-RG4KPBEZ-B4g4t9_Y.js → radar-RG4KPBEZ-Dolxtcln.js} +1 -1
- package/dist/ui/assets/{railroad-74A4TZTK-DbL2DZlM.js → railroad-74A4TZTK-WQOQGRUO.js} +1 -1
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-oeSeN2iQ.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-CXhtQ5k4.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-CZQ_zKCz.js +1 -0
- package/dist/ui/assets/{railroadDiagram-O6MQD6OU-Bq4-JY0m.js → railroadDiagram-O6MQD6OU-BvlFISOo.js} +1 -1
- package/dist/ui/assets/repo-CPWzR4oq.js +1 -0
- package/dist/ui/assets/{requirementDiagram-BXWQKSXE-CVI43xef.js → requirementDiagram-BXWQKSXE-BYKoXbW1.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-P5KCCOFB-6igeb8Iu.js → sankeyDiagram-P5KCCOFB-ClkaPNXM.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-WJ2MYXX4-LYhNaHRI.js → sequenceDiagram-WJ2MYXX4-CnLXWS23.js} +1 -1
- package/dist/ui/assets/{stateDiagram-D77RDMKH-VQcHODiY.js → stateDiagram-D77RDMKH-SURchdbJ.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-3QeI6zO7.js +1 -0
- package/dist/ui/assets/{swimlanes-42K2YHIH-C5U1l1mo.js → swimlanes-42K2YHIH-ETHZRcl1.js} +1 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-2inBpL8s.js +8 -0
- package/dist/ui/assets/{timeline-definition-24CTP7MA-C8YMVaeD.js → timeline-definition-24CTP7MA-CzmcMnEC.js} +1 -1
- package/dist/ui/assets/{treeView-Q6P3EWNA-B4Opgu8L.js → treeView-Q6P3EWNA-DpYbsVnL.js} +1 -1
- package/dist/ui/assets/{treemap-WGGIJYW6-BWpYq891.js → treemap-WGGIJYW6-CprmPbH2.js} +1 -1
- package/dist/ui/assets/{vennDiagram-4TSXK5OY-BZ7_jhEK.js → vennDiagram-4TSXK5OY-h5jxvp23.js} +1 -1
- package/dist/ui/assets/{wardley-WFR3VGLG-DSF6DKt8.js → wardley-WFR3VGLG-C9hsSVZz.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-VM6X3IG4-ChfzNSSI.js → wardleyDiagram-VM6X3IG4-D58m5Tfi.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-S5SC5T6Z-CwDvFw4R.js → xychartDiagram-S5SC5T6Z-WeCot50K.js} +1 -1
- package/dist/ui/index.html +6 -6
- package/dist/ui/sw.js +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/ChecksView-CpGuGL1P.js +0 -0
- package/dist/ui/assets/ContextView-C2ElRI0s.js +0 -4
- package/dist/ui/assets/ReleasesView-DaX_TFoT.css +0 -1
- package/dist/ui/assets/SettingsView-C1xiIbbq.js +0 -4
- package/dist/ui/assets/StoriesView-BCJnrDOc.js +0 -1
- package/dist/ui/assets/WorkView-CrQ8R4xr.js +0 -2
- package/dist/ui/assets/architecture-7GRP2DOG-BKXNQ9fY.js +0 -1
- package/dist/ui/assets/channel-DXJt6aEW.js +0 -1
- package/dist/ui/assets/classDiagram-ZZMXUADV-CuXQhOCc.js +0 -1
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-CuXQhOCc.js +0 -1
- package/dist/ui/assets/config-CxRkBtUj.js +0 -1
- package/dist/ui/assets/eventmodeling-NTZA5JFV-BXXaF1fS.js +0 -1
- package/dist/ui/assets/flowDiagram-HODETNUW-HB2tiq05.js +0 -1
- package/dist/ui/assets/index-C9k_vHM5.js +0 -46
- package/dist/ui/assets/index-DKDq8ZAh.css +0 -1
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-SDQCarTw.js +0 -1
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-D9NI_tLh.js +0 -1
- package/dist/ui/assets/railroad-peg-WCYAUIDC-kSMa3Mxd.js +0 -1
- package/dist/ui/assets/repo-OUIxzvAy.js +0 -1
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-L8aM9etd.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-Dj94QI7O.js +0 -8
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ install, `repoos upgrade` prints the matching package-manager command instead.
|
|
|
96
96
|
Then in any repo:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
repoos init # scaffold work/, docs/, repoos.toml, AGENTS.md (idempotent)
|
|
99
|
+
repoos init # scaffold repoos/work/, repoos/docs/, repoos.toml, AGENTS.md (idempotent)
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
Zero config required. `repoos init` walks up to find your repo root, creates the
|
|
@@ -219,6 +219,14 @@ your other Tailscale devices and from localhost-only tools on the same machine
|
|
|
219
219
|
(e.g. a Cloudflare Tunnel's local origin). Pass `--host 127.0.0.1` explicitly
|
|
220
220
|
to restrict it to localhost only.
|
|
221
221
|
|
|
222
|
+
In that mode `repoos serve` also runs `tailscale serve` for you and prints an
|
|
223
|
+
`https://<machine>.<tailnet>.ts.net` URL. Use it: a plain-`http://` Tailscale IP
|
|
224
|
+
isn't a browser secure context, so some UI features can't work there. It is
|
|
225
|
+
tailnet-only (not Funnel), never overwrites another `tailscale serve` mapping
|
|
226
|
+
(it falls back to port 8443, then 10000), and needs MagicDNS + HTTPS
|
|
227
|
+
Certificates enabled in the Tailscale admin console (`serve` prints a hint if
|
|
228
|
+
not). Pass `--no-tailscale-https` to skip it.
|
|
229
|
+
|
|
222
230
|
### Endpoints
|
|
223
231
|
|
|
224
232
|
```
|
package/dist/.build-info.json
CHANGED
package/dist/.build-stamp.json
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { cmdList, cmdShow, cmdMv, cmdUpdate, cmdNew, cmdIndex, cmdNote, } from "
|
|
|
8
8
|
import { cmdNewDoc } from "../commands/docs.js";
|
|
9
9
|
import { cmdGc } from "../commands/gc.js";
|
|
10
10
|
import { cmdCheck } from "../commands/check.js";
|
|
11
|
+
import { cmdShot } from "../commands/shot.js";
|
|
11
12
|
import { cmdServe, serveProcessTitle, setServeProcessTitle } from "../commands/serve.js";
|
|
12
13
|
import { cmdStop } from "../commands/stop.js";
|
|
13
14
|
import { cmdTunnel } from "../commands/tunnel.js";
|
|
@@ -22,9 +23,7 @@ import { checkBuild } from "../core/build.js";
|
|
|
22
23
|
import { loadConfig } from "../core/config.js";
|
|
23
24
|
import { reexecAfterStaleBuild, reexecUnderBunIfRequested } from "../core/runtime.js";
|
|
24
25
|
import { c } from "./colors.js";
|
|
25
|
-
import {
|
|
26
|
-
import { dirname, join } from "node:path";
|
|
27
|
-
import { fileURLToPath } from "node:url";
|
|
26
|
+
import { readVersion } from "../core/version.js";
|
|
28
27
|
// node:sqlite (used by db.ts/auth-store.ts) is still marked experimental on
|
|
29
28
|
// supported Node versions and prints a warning the first time it's loaded.
|
|
30
29
|
// That's expected/stable usage here, not something a user needs to see —
|
|
@@ -39,19 +38,6 @@ process.on("warning", (warning) => {
|
|
|
39
38
|
return;
|
|
40
39
|
console.warn(warning);
|
|
41
40
|
});
|
|
42
|
-
/** Reads the version stamped into .build-info.json at build time; falls back for dev/source runs. */
|
|
43
|
-
function readVersion() {
|
|
44
|
-
try {
|
|
45
|
-
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
46
|
-
const info = JSON.parse(readFileSync(join(root, ".build-info.json"), "utf8"));
|
|
47
|
-
if (info.version)
|
|
48
|
-
return info.version;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
/* fall through */
|
|
52
|
-
}
|
|
53
|
-
return "0.0.0-dev";
|
|
54
|
-
}
|
|
55
41
|
const VERSION = readVersion();
|
|
56
42
|
// Column where COMMANDS descriptions start, measured from the start of the
|
|
57
43
|
// command text (not counting the leading 4-space indent). Wide enough for
|
|
@@ -89,6 +75,7 @@ function help() {
|
|
|
89
75
|
cmdRow("check --profile full", "Run every declared step, including ones held back from a routine run"),
|
|
90
76
|
cmdRow("check --changed main", "Fast pre-review pass over steps affected by changed paths"),
|
|
91
77
|
cmdRow("check --print-plan", "Print the resolved plan as [[check.steps]] TOML to commit"),
|
|
78
|
+
cmdRow("shot [<route|url>]", `Capture preview screenshots into the task's shots folder ${c.dim("flags: --target --selector --task --base --wait --full-page")}`),
|
|
92
79
|
cmdRow("init [name]", "Scaffold work/, repoos.toml, AGENTS.md; outside a git repo runs a guided flow that can launch the web console"),
|
|
93
80
|
cmdRow("list [status]", `Show the board (or one column: ${c.dim("inbox|ready|active|review|done")})`),
|
|
94
81
|
cmdRow("status [--json]", "One-screen health snapshot: server, build freshness, board, worktrees, tunnel, git"),
|
|
@@ -103,7 +90,7 @@ function help() {
|
|
|
103
90
|
cmdRow('new-doc "<desc>"', "Create a document from a description via PM agent"),
|
|
104
91
|
cmdRow("index [--json]", "Rebuild the derived index cache"),
|
|
105
92
|
cmdRow("gc [--yes|--dry-run]", "Collect leaked task worktrees/branches (done/absent tasks, integrate candidates)"),
|
|
106
|
-
cmdRow("serve [--port N]", `Start the local server (live API + SSE stream) ${c.dim("flags: --host, --preview-overrides, --no-preview-overrides")}`),
|
|
93
|
+
cmdRow("serve [--port N]", `Start the local server (live API + SSE stream) ${c.dim("flags: --host, --no-tailscale-https, --preview-overrides, --no-preview-overrides")}`),
|
|
107
94
|
cmdRow("stop [--port N]", "Stop this repo's serve process (by its own lockfile)"),
|
|
108
95
|
cmdRow("service [sub]", `Manage background services ${c.dim("list|status|install|start|stop|restart|enable|disable|remove")}`),
|
|
109
96
|
cmdRow("tunnel <sub>", `Publish local apps via Cloudflare Tunnel + Zero Trust ${c.dim("(setup|create|allow|deny|rename|destroy|start|install|stop|list|status)")}`),
|
|
@@ -113,13 +100,16 @@ function help() {
|
|
|
113
100
|
const exampleLines = [
|
|
114
101
|
exRow("repoos init"),
|
|
115
102
|
exRow("repoos init myproject", "guided new-project flow outside a git repo"),
|
|
116
|
-
exRow('repoos new "Add company dashboard" --ai --type feature --area web --priority p1'),
|
|
103
|
+
exRow('repoos new "Add company dashboard" --ai --type feature --area web,server --priority p1'),
|
|
117
104
|
exRow('repoos new-doc "API design doc for the payment system"'),
|
|
118
105
|
exRow("repoos mv 0012 active"),
|
|
119
106
|
exRow('repoos mv 0012 active --note "Fix the regression in checkout; see review"'),
|
|
120
107
|
exRow('repoos note 0012 "Handle the reviewer\'s suggestions before the next review"'),
|
|
121
|
-
exRow('repoos update 0012 --title "New title" --area web'),
|
|
108
|
+
exRow('repoos update 0012 --title "New title" --area web,core'),
|
|
122
109
|
exRow("repoos list ready"),
|
|
110
|
+
exRow("repoos shot", "screenshot the task preview for the current worktree"),
|
|
111
|
+
exRow("repoos shot /repo/commits/abc123", "capture one route"),
|
|
112
|
+
exRow("repoos shot --target 'Docs site' --selector '.sidebar'"),
|
|
123
113
|
exRow("repoos status", "one-screen health snapshot"),
|
|
124
114
|
exRow("repoos status --json", "machine-readable, for agents/tools"),
|
|
125
115
|
exRow("repoos index --json", "machine-readable, for agents/tools"),
|
|
@@ -264,6 +254,17 @@ function main() {
|
|
|
264
254
|
case "check":
|
|
265
255
|
void cmdCheck(rest);
|
|
266
256
|
break;
|
|
257
|
+
case "shot":
|
|
258
|
+
void cmdShot(rest)
|
|
259
|
+
.then((code) => {
|
|
260
|
+
if (code !== 0)
|
|
261
|
+
process.exitCode = code;
|
|
262
|
+
})
|
|
263
|
+
.catch((err) => {
|
|
264
|
+
console.error(c.red(` ✗ shot failed: ${err.message}`));
|
|
265
|
+
process.exitCode = 1;
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
267
268
|
case "tunnel":
|
|
268
269
|
void cmdTunnel(rest);
|
|
269
270
|
break;
|
package/dist/commands/check.js
CHANGED
|
@@ -31,6 +31,7 @@ import { preferBunForDevTasks } from "../core/runtime.js";
|
|
|
31
31
|
import { blockingFailures, DEFAULT_PROFILE, describeStep, formatPlanToml, resolveCheckPlan, selectSteps, } from "../core/check-plan.js";
|
|
32
32
|
import { detectRepoMarkers, missingBinaries, prereqDetail, runCommand, stepCwd, } from "../core/check-runner.js";
|
|
33
33
|
import { writeCheckRun } from "../core/check-results-store.js";
|
|
34
|
+
import { extractFailedTests } from "../core/check-failure-summary.js";
|
|
34
35
|
import { envToRunContext, getCheckStore, localMachineName } from "../core/check-store.js";
|
|
35
36
|
import { mainCheckoutRoot } from "../core/git.js";
|
|
36
37
|
import { Logger } from "../core/logger.js";
|
|
@@ -1640,6 +1641,9 @@ export async function cmdCheck(argv = []) {
|
|
|
1640
1641
|
failedStep: gatingFailures[0]?.name ?? null,
|
|
1641
1642
|
skippedSteps: results.filter((r) => r.status === "skipped").map((r) => r.name),
|
|
1642
1643
|
detail: gatingFailures[0]?.detail ?? null,
|
|
1644
|
+
// The stored detail is a short log tail that routinely loses the failing
|
|
1645
|
+
// test's name; keep the names themselves so "which test?" is a query.
|
|
1646
|
+
failedTests: gatingFailures.flatMap((r) => extractFailedTests(r.output ?? "")),
|
|
1643
1647
|
});
|
|
1644
1648
|
process.exit(gatingFailures.length > 0 ? 1 : 0);
|
|
1645
1649
|
}
|
|
@@ -1672,6 +1676,7 @@ function recordRunHistoryRow(row) {
|
|
|
1672
1676
|
failedStep: row.failedStep,
|
|
1673
1677
|
skippedSteps: row.skippedSteps,
|
|
1674
1678
|
detail: row.detail,
|
|
1679
|
+
failedTests: row.failedTests,
|
|
1675
1680
|
});
|
|
1676
1681
|
}
|
|
1677
1682
|
catch {
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Blockquote every line, so a multi-line (pasted markdown) description stays one quote. */
|
|
2
|
+
export declare function quoteBlock(text: string): string;
|
|
1
3
|
/** Marker makes the optional existing-repo addition safe to offer repeatedly. */
|
|
2
4
|
export declare const REPOOS_AGENTS_SECTION_MARKER = "<!-- repoos:managed-instructions -->";
|
|
3
5
|
/**
|
|
@@ -19,7 +21,11 @@ export declare function repoOSAgentsSectionAddition(existing: string, workDir?:
|
|
|
19
21
|
type ScaffoldLayout = string;
|
|
20
22
|
/** Which starter task to seed beyond 0001 — a blank project or an existing repo. */
|
|
21
23
|
type ScaffoldKind = "new" | "existing";
|
|
22
|
-
export declare function scaffoldInto(root: string, description: string, layout?: ScaffoldLayout, kind?: ScaffoldKind
|
|
24
|
+
export declare function scaffoldInto(root: string, description: string, layout?: ScaffoldLayout, kind?: ScaffoldKind,
|
|
25
|
+
/** Areas collected at init time (#0583) — real `[[areas]]` rows when given. */
|
|
26
|
+
areas?: string[],
|
|
27
|
+
/** When true, scaffold commented `[[preview.targets]]` stubs for those areas. */
|
|
28
|
+
previewStub?: boolean): {
|
|
23
29
|
created: string[];
|
|
24
30
|
skipped: string[];
|
|
25
31
|
};
|
package/dist/commands/init.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* guided, interactive flow that creates a brand-new RepoOS project from
|
|
7
7
|
* scratch: optionally in a subdirectory, with a choice of layout (repo root
|
|
8
8
|
* vs. a `repoos/` subfolder, config file at root either way), an optional
|
|
9
|
-
*
|
|
9
|
+
* project description, one or many lines (seeded into the sample task) and an optional
|
|
10
10
|
* initial commit.
|
|
11
11
|
*/
|
|
12
12
|
import { spawn } from "node:child_process";
|
|
@@ -15,6 +15,8 @@ import { createServer } from "node:net";
|
|
|
15
15
|
import { join, resolve } from "node:path";
|
|
16
16
|
import { createInterface } from "node:readline/promises";
|
|
17
17
|
import { stdin as input, stdout as output } from "node:process";
|
|
18
|
+
import { canaryGitignoreIgnore, canaryGitignoreNegation, canaryRelPath } from "../core/canary.js";
|
|
19
|
+
import { scaffoldCanaryFile } from "../core/canary-repo.js";
|
|
18
20
|
import { deriveServePort, findRepoRoot, loadConfig } from "../core/config.js";
|
|
19
21
|
import { CHECK_PLAN_PROPOSAL_FILE, proposeCheckPlan } from "../core/check-plan-proposal.js";
|
|
20
22
|
import { formatPlanToml } from "../core/check-plan.js";
|
|
@@ -58,12 +60,19 @@ running \`repoos init\` already satisfies everything above. There's nothing to
|
|
|
58
60
|
Status is a frontmatter field — never move files between folders. Keep diffs
|
|
59
61
|
small. Read AGENTS.md before starting any task.
|
|
60
62
|
`;
|
|
63
|
+
/** Blockquote every line, so a multi-line (pasted markdown) description stays one quote. */
|
|
64
|
+
export function quoteBlock(text) {
|
|
65
|
+
return text
|
|
66
|
+
.split("\n")
|
|
67
|
+
.map((line) => (line.trim() === "" ? ">" : `> ${line}`))
|
|
68
|
+
.join("\n");
|
|
69
|
+
}
|
|
61
70
|
/**
|
|
62
71
|
* The first *workable* task a guided new-project init leaves on the board.
|
|
63
72
|
* 0001 is deliberately `done` (scaffolding is the whole of it), so this is what
|
|
64
73
|
* a new user — or their first agent — actually picks up. `repoos init` runs
|
|
65
74
|
* before any server or agent exists, so the body itself is the prompt: it
|
|
66
|
-
* embeds the
|
|
75
|
+
* embeds the description collected at init and carries the questions
|
|
67
76
|
* that turn that description into docs and real tasks later.
|
|
68
77
|
*/
|
|
69
78
|
const NEW_PROJECT_STARTER_TASK = (id, description, workDir = "work", docsDir = "docs") => `---
|
|
@@ -79,15 +88,15 @@ branch: ""
|
|
|
79
88
|
---
|
|
80
89
|
## Overview
|
|
81
90
|
|
|
82
|
-
This project started from a
|
|
91
|
+
This project started from a short description. This task turns it into
|
|
83
92
|
something a team can actually build from — a shared vision, an initial
|
|
84
93
|
architecture, and a first batch of concrete work.
|
|
85
94
|
|
|
86
95
|
## Project description
|
|
87
96
|
|
|
88
97
|
${description
|
|
89
|
-
?
|
|
90
|
-
: "_No description was given at init time. Start by writing
|
|
98
|
+
? quoteBlock(description)
|
|
99
|
+
: "_No description was given at init time. Start by writing a short description that says what this project is and who it's for._"}
|
|
91
100
|
|
|
92
101
|
## What to do
|
|
93
102
|
|
|
@@ -202,6 +211,15 @@ a candidate merge. Do not erase a task's branch metadata to force this path.
|
|
|
202
211
|
|
|
203
212
|
## Rules
|
|
204
213
|
|
|
214
|
+
- **\`repoos.toml\` owns the layout.** \`workDir\` (\`${workDir}\`) and \`docsDir\`
|
|
215
|
+
(\`${docsDir}\`) are authoritative: tasks live under \`${workDir}/\` and project
|
|
216
|
+
docs under \`${docsDir}/\` because the config says so. Never move, rename or
|
|
217
|
+
relocate those directories, and never "fix" a layout you think is wrong by
|
|
218
|
+
moving files. If project docs or a person's own notes disagree with the
|
|
219
|
+
config, report the mismatch — the fix is to update \`repoos.toml\` with human
|
|
220
|
+
approval (or correct the docs), never to move directories on your own. Do not
|
|
221
|
+
invent ownership rules for RepoOS directories; the config is the only source
|
|
222
|
+
of truth for layout.
|
|
205
223
|
- **Never** move task files between folders. Status lives in frontmatter.
|
|
206
224
|
- **Never write directly to \`${workDir}/*.md\` files.** All task creation and
|
|
207
225
|
manipulation goes through \`repoos\` commands or HTTP API endpoints
|
|
@@ -249,7 +267,10 @@ export const REPOOS_AGENTS_SECTION = (workDir) => `${REPOOS_AGENTS_SECTION_MARKE
|
|
|
249
267
|
## RepoOS
|
|
250
268
|
|
|
251
269
|
RepoOS keeps tasks as Markdown under \`${workDir}/\` and runs task work in dedicated
|
|
252
|
-
Git worktrees.
|
|
270
|
+
Git worktrees. The layout is set by \`repoos.toml\` (\`workDir\`/\`docsDir\`/\`cacheDir\`):
|
|
271
|
+
those paths are authoritative, so never move or rename them — if project docs
|
|
272
|
+
disagree with the config, ask the human instead of relocating directories.
|
|
273
|
+
Use the RepoOS UI or \`repoos\` commands to create and update
|
|
253
274
|
tasks; do not hand-edit task files. Read the relevant project docs before
|
|
254
275
|
starting work, run \`repoos check\` before handoff, and await human approval
|
|
255
276
|
through RepoOS's **Move to done**. The reviewer is advisory. A Git remote does
|
|
@@ -267,7 +288,7 @@ export function repoOSAgentsSectionAddition(existing, workDir = "work") {
|
|
|
267
288
|
}
|
|
268
289
|
return (existing.endsWith("\n") ? "\n" : "\n\n") + REPOOS_AGENTS_SECTION(workDir);
|
|
269
290
|
}
|
|
270
|
-
function repoosToml(namespace) {
|
|
291
|
+
function repoosToml(namespace, areas = [], previewStub = false) {
|
|
271
292
|
const ns = namespace
|
|
272
293
|
? `workDir = "${namespace}/work"\ndocsDir = "${namespace}/docs"\ncacheDir = "${namespace}/.repoos"\n`
|
|
273
294
|
: "";
|
|
@@ -305,8 +326,45 @@ defaultAssignee = "unassigned"
|
|
|
305
326
|
# active = "In progress"
|
|
306
327
|
# review = "Code review"
|
|
307
328
|
# done = "Shipped"
|
|
329
|
+
${areaRows(areas)}
|
|
330
|
+
${previewStub ? previewTargetRows(areas) : ""}
|
|
308
331
|
`;
|
|
309
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* The `[areas]` block: the vocabulary collected at init time as real
|
|
335
|
+
* `[[areas]]` rows, or the commented-out stub when skipped (#0583).
|
|
336
|
+
*/
|
|
337
|
+
function areaRows(areas) {
|
|
338
|
+
if (!areas.length) {
|
|
339
|
+
return [
|
|
340
|
+
"# [[areas]] # the task-area vocabulary (#0583)",
|
|
341
|
+
'# name = "web" # offered in the area picker + the PM prompt',
|
|
342
|
+
'# description = "The main web app"',
|
|
343
|
+
"# Every [[preview.targets]] area is offered automatically, even when not",
|
|
344
|
+
"# declared here. With none declared the area field is free text only.",
|
|
345
|
+
].join("\n");
|
|
346
|
+
}
|
|
347
|
+
return areas.map((a) => `[[areas]]\nname = "${a}"`).join("\n\n");
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The commented `[[preview.targets]]` skeleton offered when an area vocabulary
|
|
351
|
+
* was seeded at init (#0583) — the wiring for when the project has something
|
|
352
|
+
* previewable, referencing the areas the picker now offers.
|
|
353
|
+
*/
|
|
354
|
+
function previewTargetRows(areas) {
|
|
355
|
+
const areaList = areas.length ? JSON.stringify(areas) : '["web"]';
|
|
356
|
+
return [
|
|
357
|
+
"# [preview] # read-only task previews",
|
|
358
|
+
`# command = "bun run dev --port {port} --host {host}"`,
|
|
359
|
+
`# # default when no target matches`,
|
|
360
|
+
"",
|
|
361
|
+
`# [[preview.targets]]`,
|
|
362
|
+
`# name = "Main app"`,
|
|
363
|
+
`# areas = ${areaList}`,
|
|
364
|
+
`# command = "bun run dev --port {port} --host {host}"`,
|
|
365
|
+
`# readyTimeoutMs = 240000 # raise for a command that also builds`,
|
|
366
|
+
].join("\n");
|
|
367
|
+
}
|
|
310
368
|
const ENV_EXAMPLE = `# Copy to .env and fill in what you need — .env is gitignored, this file is
|
|
311
369
|
# tracked so the repo documents which secrets a full setup expects.
|
|
312
370
|
# repoos serve auto-loads .env at startup; real shell/process-supervisor env
|
|
@@ -380,7 +438,11 @@ function findStarter(root, workDir, slug) {
|
|
|
380
438
|
return null;
|
|
381
439
|
}
|
|
382
440
|
}
|
|
383
|
-
export function scaffoldInto(root, description, layout = "", kind = "new"
|
|
441
|
+
export function scaffoldInto(root, description, layout = "", kind = "new",
|
|
442
|
+
/** Areas collected at init time (#0583) — real `[[areas]]` rows when given. */
|
|
443
|
+
areas = [],
|
|
444
|
+
/** When true, scaffold commented `[[preview.targets]]` stubs for those areas. */
|
|
445
|
+
previewStub = false) {
|
|
384
446
|
const created = [];
|
|
385
447
|
const skipped = [];
|
|
386
448
|
let aborted = false;
|
|
@@ -425,7 +487,7 @@ export function scaffoldInto(root, description, layout = "", kind = "new") {
|
|
|
425
487
|
// Write config first so workDir/docsDir/cacheDir overrides are in effect
|
|
426
488
|
// before the dirs are created (namespace layout) — or so an existing
|
|
427
489
|
// config is respected untouched (existing-repo path).
|
|
428
|
-
ensureFile("repoos.toml", repoosToml(layout));
|
|
490
|
+
ensureFile("repoos.toml", repoosToml(layout, areas, previewStub));
|
|
429
491
|
const config = loadConfig(root);
|
|
430
492
|
ensureDir(config.workDir);
|
|
431
493
|
ensureDir(config.docsDir);
|
|
@@ -449,10 +511,17 @@ export function scaffoldInto(root, description, layout = "", kind = "new") {
|
|
|
449
511
|
ensureFile(join(config.workDir, `${starterId}-${starter.slug}.md`), starter.build(starterId, description, config.workDir, config.docsDir));
|
|
450
512
|
}
|
|
451
513
|
ensureFile(".env.example", ENV_EXAMPLE);
|
|
514
|
+
if (scaffoldCanaryFile(root, config.cacheDir)) {
|
|
515
|
+
created.push(canaryRelPath(config.cacheDir));
|
|
516
|
+
}
|
|
452
517
|
// gitignore the derived cache and local secrets
|
|
453
518
|
const giPath = join(root, ".gitignore");
|
|
454
519
|
const ignoreLines = [
|
|
455
|
-
{ comment: "# RepoOS derived index cache", line:
|
|
520
|
+
{ comment: "# RepoOS derived index cache", line: canaryGitignoreIgnore(config.cacheDir) },
|
|
521
|
+
{
|
|
522
|
+
comment: "# RepoOS canary flow-test counter (tracked)",
|
|
523
|
+
line: canaryGitignoreNegation(config.cacheDir),
|
|
524
|
+
},
|
|
456
525
|
{ comment: "# Local secrets — see .env.example", line: ".env" },
|
|
457
526
|
];
|
|
458
527
|
const existingLines = existsSync(giPath) ? readFileSync(giPath, "utf8").split(/\r?\n/) : [];
|
|
@@ -521,6 +590,46 @@ async function ask(question) {
|
|
|
521
590
|
rl.close();
|
|
522
591
|
}
|
|
523
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* Like `ask`, but accepts multi-line input. On a TTY, a pasted block arrives as
|
|
595
|
+
* a burst of lines, so after each line we wait briefly for more; a typed line
|
|
596
|
+
* ending in `\\` also continues onto the next line. Non-TTY input (pipes, CI)
|
|
597
|
+
* stays strictly one line so later prompts' answers aren't swallowed.
|
|
598
|
+
*/
|
|
599
|
+
async function askMultiline(question) {
|
|
600
|
+
if (!input.isTTY)
|
|
601
|
+
return ask(question);
|
|
602
|
+
const rl = createInterface({ input, output });
|
|
603
|
+
return new Promise((resolvePromise) => {
|
|
604
|
+
const lines = [];
|
|
605
|
+
let timer;
|
|
606
|
+
let done = false;
|
|
607
|
+
const finish = () => {
|
|
608
|
+
if (done)
|
|
609
|
+
return;
|
|
610
|
+
done = true;
|
|
611
|
+
if (timer)
|
|
612
|
+
clearTimeout(timer);
|
|
613
|
+
rl.close();
|
|
614
|
+
resolvePromise(lines.join("\n").trim());
|
|
615
|
+
};
|
|
616
|
+
rl.on("close", finish);
|
|
617
|
+
rl.on("line", (line) => {
|
|
618
|
+
if (timer)
|
|
619
|
+
clearTimeout(timer);
|
|
620
|
+
if (line.endsWith("\\")) {
|
|
621
|
+
lines.push(line.slice(0, -1));
|
|
622
|
+
rl.setPrompt(" … ");
|
|
623
|
+
rl.prompt();
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
lines.push(line);
|
|
627
|
+
timer = setTimeout(finish, 75);
|
|
628
|
+
});
|
|
629
|
+
rl.setPrompt(question);
|
|
630
|
+
rl.prompt();
|
|
631
|
+
});
|
|
632
|
+
}
|
|
524
633
|
async function confirm(question, dflt) {
|
|
525
634
|
const hint = dflt ? " [Y/n]" : " [y/N]";
|
|
526
635
|
const answer = (await ask(question + c.dim(hint) + " ")).toLowerCase();
|
|
@@ -854,9 +963,34 @@ async function guidedNewRepo(args) {
|
|
|
854
963
|
console.log(c.yellow("\n Cancelled — nothing was created."));
|
|
855
964
|
return;
|
|
856
965
|
}
|
|
857
|
-
const description = await
|
|
858
|
-
c.dim(" —
|
|
966
|
+
const description = await askMultiline(" Project description" +
|
|
967
|
+
c.dim(" — gives the AI context to suggest next steps; paste multi-line markdown or end a line with \\ to continue (optional, Enter to skip)") +
|
|
968
|
+
": ");
|
|
969
|
+
// The task-area vocabulary (#0583) — reinforced with preview targets, since
|
|
970
|
+
// a `[[preview.targets]]` area is offered in the picker automatically even
|
|
971
|
+
// when not declared here. Skipped means free text only; never blocking.
|
|
972
|
+
const areasInput = await ask(" Task areas to seed the area picker" +
|
|
973
|
+
c.dim(" — comma-separated (e.g. web, cli, api; Enter to skip)") +
|
|
859
974
|
": ");
|
|
975
|
+
const areas = areasInput
|
|
976
|
+
? areasInput
|
|
977
|
+
.split(",")
|
|
978
|
+
.map((a) => a.trim())
|
|
979
|
+
.filter(Boolean)
|
|
980
|
+
: [];
|
|
981
|
+
if (areas.length) {
|
|
982
|
+
console.log(c.dim(" Preview targets route a task preview by its area — the picker and"));
|
|
983
|
+
console.log(c.dim(" the PM prompt offer target areas automatically; see user-docs/configuration.md's Previews section."));
|
|
984
|
+
}
|
|
985
|
+
// Preview targets reinforce areas ("define them together"): when an area
|
|
986
|
+
// vocabulary was given, offer to scaffold the commented `[[preview.targets]]`
|
|
987
|
+
// skeleton those areas feed, so the wiring sits where preview setup happens
|
|
988
|
+
// later. A brand-new project has nothing runnable yet — never a live
|
|
989
|
+
// preview command, just the commented shape to fill in.
|
|
990
|
+
let previewStub = false;
|
|
991
|
+
if (areas.length) {
|
|
992
|
+
previewStub = await confirm(" Scaffold commented preview-target stubs for these areas?", false);
|
|
993
|
+
}
|
|
860
994
|
if (!existsSync(target))
|
|
861
995
|
mkdirSync(target, { recursive: true });
|
|
862
996
|
// git health warnings — fail-soft, scaffold regardless
|
|
@@ -869,7 +1003,7 @@ async function guidedNewRepo(args) {
|
|
|
869
1003
|
console.log(c.dim(" git may auto-detect it, or the initial commit may fail. Set it with:"));
|
|
870
1004
|
console.log(c.dim(' git config --global user.name "You" && git config --global user.email you@example.com'));
|
|
871
1005
|
}
|
|
872
|
-
const { created, skipped } = scaffoldInto(target, description, layout);
|
|
1006
|
+
const { created, skipped } = scaffoldInto(target, description, layout, "new", areas, previewStub);
|
|
873
1007
|
reportInit(target, created, skipped);
|
|
874
1008
|
await offerCheckPlanProposal(target);
|
|
875
1009
|
if (!gitOk) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { renderServeBanner } from "./serve.js";
|
|
3
|
+
describe("renderServeBanner", () => {
|
|
4
|
+
const rows = [
|
|
5
|
+
{ label: "Local", value: "http://127.0.0.1:7515" },
|
|
6
|
+
{ label: "Tailnet HTTPS", value: "x" },
|
|
7
|
+
];
|
|
8
|
+
it("shows the version and aligns labels", () => {
|
|
9
|
+
const out = renderServeBanner("1.2.3", rows);
|
|
10
|
+
expect(out).toContain("RepoOS v1.2.3");
|
|
11
|
+
expect(out).toContain("Local http://127.0.0.1:7515");
|
|
12
|
+
});
|
|
13
|
+
it("splits an HTTPS warning into problem, fix and link", () => {
|
|
14
|
+
const out = renderServeBanner("1.2.3", rows, "HTTPS certificates aren't enabled for your tailnet — turn on MagicDNS and HTTPS Certificates at https://login.tailscale.com/admin/dns");
|
|
15
|
+
expect(out).toContain("Tailscale HTTPS unavailable");
|
|
16
|
+
expect(out).toContain("HTTPS certificates aren't enabled for your tailnet");
|
|
17
|
+
expect(out).toContain("Fix: turn on MagicDNS and HTTPS Certificates at");
|
|
18
|
+
expect(out).toContain("https://login.tailscale.com/admin/dns");
|
|
19
|
+
expect(out).toContain("--no-tailscale-https");
|
|
20
|
+
});
|
|
21
|
+
it("handles a reason without a fix link", () => {
|
|
22
|
+
const out = renderServeBanner("1", rows, "all ports in use");
|
|
23
|
+
expect(out).toContain("all ports in use");
|
|
24
|
+
expect(out).not.toContain("Fix:");
|
|
25
|
+
});
|
|
26
|
+
});
|
package/dist/commands/serve.d.ts
CHANGED
|
@@ -59,6 +59,18 @@ export declare function resolvePreviewOverrides(args: readonly string[], env?: N
|
|
|
59
59
|
* processes keep working. Exported for the defense-in-depth tests (#0096).
|
|
60
60
|
*/
|
|
61
61
|
export declare function directServeBlockedByAgent(env?: NodeJS.ProcessEnv): boolean;
|
|
62
|
+
export interface BannerRow {
|
|
63
|
+
label: string;
|
|
64
|
+
/** May contain ANSI codes. */
|
|
65
|
+
value: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The startup banner: a title line with the running version, an aligned
|
|
69
|
+
* label/value table, and — when Tailscale HTTPS could not be set up — a
|
|
70
|
+
* highlighted warning block that separates the problem from its fix. Pure
|
|
71
|
+
* (returns a string) so tests can assert on it with colors off.
|
|
72
|
+
*/
|
|
73
|
+
export declare function renderServeBanner(version: string, rows: readonly BannerRow[], httpsWarning?: string): string;
|
|
62
74
|
export declare function cmdServe(args: string[], opts?: {
|
|
63
75
|
onShutdown?: () => void;
|
|
64
76
|
onReady?: (url: string) => void;
|
package/dist/commands/serve.js
CHANGED
|
@@ -8,7 +8,8 @@ import { PREVIEW_REQUEST_SIGNAL } from "../server/agents.js";
|
|
|
8
8
|
import { isBun } from "../core/runtime.js";
|
|
9
9
|
import { findRepoRoot } from "../core/config.js";
|
|
10
10
|
import { c, statusColor } from "../cli/colors.js";
|
|
11
|
-
import {
|
|
11
|
+
import { readVersion } from "../core/version.js";
|
|
12
|
+
import { detectTailscaleIPv4, ensureTailscaleHttps } from "../core/tailscale.js";
|
|
12
13
|
/**
|
|
13
14
|
* The OS-visible process title for a `repoos serve` instance:
|
|
14
15
|
* `repoos-<project>`, where `<project>` is the managed root's directory name.
|
|
@@ -109,6 +110,39 @@ export function resolvePreviewOverrides(args, env = process.env) {
|
|
|
109
110
|
export function directServeBlockedByAgent(env = process.env) {
|
|
110
111
|
return env.REPOOS_AGENT === "1" && env.REPOOS_PREVIEW_CHILD !== "1" && env.REPOOS_RELOAD !== "1";
|
|
111
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* The startup banner: a title line with the running version, an aligned
|
|
115
|
+
* label/value table, and — when Tailscale HTTPS could not be set up — a
|
|
116
|
+
* highlighted warning block that separates the problem from its fix. Pure
|
|
117
|
+
* (returns a string) so tests can assert on it with colors off.
|
|
118
|
+
*/
|
|
119
|
+
export function renderServeBanner(version, rows, httpsWarning) {
|
|
120
|
+
const width = Math.max(...rows.map((r) => r.label.length));
|
|
121
|
+
const out = [
|
|
122
|
+
"",
|
|
123
|
+
` ${c.bold(c.cyan("◆ RepoOS"))} ${c.dim("v" + version)} ${c.green("● running")}`,
|
|
124
|
+
"",
|
|
125
|
+
];
|
|
126
|
+
for (const r of rows)
|
|
127
|
+
out.push(` ${c.dim(r.label.padEnd(width))} ${r.value}`);
|
|
128
|
+
if (httpsWarning) {
|
|
129
|
+
// Reasons read "<problem> — <fix> <url>": put the problem, the fix and the
|
|
130
|
+
// link on their own lines so the actionable part isn't buried.
|
|
131
|
+
const [problem, ...rest] = httpsWarning.split(" — ");
|
|
132
|
+
const fixText = rest.join(" — ");
|
|
133
|
+
const url = /https?:\/\/\S+/.exec(fixText)?.[0];
|
|
134
|
+
const fix = (url ? fixText.replace(url, "") : fixText).replace(/\s*\bat\s*$/, "").trim();
|
|
135
|
+
out.push("", ` ${c.yellow("▲")} ${c.bold(c.yellow("Tailscale HTTPS unavailable"))}`);
|
|
136
|
+
out.push(` ${c.dim(problem ?? "")}`);
|
|
137
|
+
if (fix)
|
|
138
|
+
out.push(` ${c.bold("Fix:")} ${fix}${url ? c.dim(" at") : ""}`);
|
|
139
|
+
if (url)
|
|
140
|
+
out.push(` ${c.cyan(url)}`);
|
|
141
|
+
out.push(` ${c.dim("or pass")} ${c.cyan("--no-tailscale-https")} ${c.dim("to silence this")}`);
|
|
142
|
+
}
|
|
143
|
+
out.push("", c.dim(" press ^C to stop"), "");
|
|
144
|
+
return out.join("\n");
|
|
145
|
+
}
|
|
112
146
|
export async function cmdServe(args, opts = {}) {
|
|
113
147
|
// Defense in depth (#0096): a managed agent process must never start its own
|
|
114
148
|
// `repoos serve`. An agent that ignores the mission and runs `repoos serve`
|
|
@@ -128,6 +162,7 @@ export async function cmdServe(args, opts = {}) {
|
|
|
128
162
|
let port;
|
|
129
163
|
let explicitHost;
|
|
130
164
|
let quiet = false;
|
|
165
|
+
let tailscaleHttps = true;
|
|
131
166
|
for (let i = 0; i < args.length; i++) {
|
|
132
167
|
if (args[i] === "--port" || args[i] === "-p")
|
|
133
168
|
port = Number(args[++i]);
|
|
@@ -135,6 +170,8 @@ export async function cmdServe(args, opts = {}) {
|
|
|
135
170
|
explicitHost = args[++i];
|
|
136
171
|
else if (args[i] === "--quiet" || args[i] === "-q")
|
|
137
172
|
quiet = true;
|
|
173
|
+
else if (args[i] === "--no-tailscale-https")
|
|
174
|
+
tailscaleHttps = false;
|
|
138
175
|
}
|
|
139
176
|
// Preview-only config overlay (#0464). A managed preview child
|
|
140
177
|
// (REPOOS_PREVIEW_CHILD=1, set by PreviewManager) applies the repo's
|
|
@@ -171,32 +208,46 @@ export async function cmdServe(args, opts = {}) {
|
|
|
171
208
|
return;
|
|
172
209
|
}
|
|
173
210
|
const snap = handle.index.snapshot();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
c.cyan(handle.url + "/api/events"))
|
|
211
|
+
const rt = isBun()
|
|
212
|
+
? `Bun ${process.versions.bun ?? ""}`.trim()
|
|
213
|
+
: `Node ${process.versions.node}`;
|
|
214
|
+
const rows = [
|
|
215
|
+
{ label: "Local", value: c.cyan(c.bold(handle.url)) },
|
|
216
|
+
{ label: "Events", value: c.cyan(handle.url + "/api/events") + c.dim(" (SSE)") },
|
|
217
|
+
{ label: "Watching", value: `${snap.taskCount} tasks` },
|
|
218
|
+
{ label: "Runtime", value: rt },
|
|
219
|
+
];
|
|
180
220
|
// Report an active preview-only overlay and its effective keys (#0464), so a
|
|
181
221
|
// preview that deliberately differs from the base config is never silent.
|
|
182
222
|
if (handle.previewOverrides?.length) {
|
|
183
|
-
|
|
223
|
+
rows.push({ label: "Preview", value: c.yellow(handle.previewOverrides.join(", ")) });
|
|
184
224
|
}
|
|
185
|
-
const rt = isBun()
|
|
186
|
-
? `Bun ${process.versions.bun ?? ""}`.trim()
|
|
187
|
-
: `Node ${process.versions.node}`;
|
|
188
|
-
console.log(c.dim(" runtime ") + rt);
|
|
189
|
-
console.log(c.dim(" api: ") + c.dim("/api/tasks /api/tasks/:id /api/counts /api/index /api/docs"));
|
|
190
225
|
if (tailscaleDetected) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
c.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
226
|
+
rows.push({
|
|
227
|
+
label: "Tailnet",
|
|
228
|
+
value: c.cyan(`http://${tailscaleIP}:${handle.port}`) +
|
|
229
|
+
c.dim(" (all interfaces; ") +
|
|
230
|
+
c.cyan("--host 127.0.0.1") +
|
|
231
|
+
c.dim(" to restrict)"),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// HTTPS for tailnet devices (a plain-http IP isn't a secure context, so
|
|
235
|
+
// crypto.randomUUID etc. are missing). Skipped for managed previews, which
|
|
236
|
+
// must not touch the machine's `tailscale serve` config.
|
|
237
|
+
let httpsWarning;
|
|
238
|
+
if (tailscaleDetected && tailscaleHttps && process.env.REPOOS_PREVIEW_CHILD !== "1") {
|
|
239
|
+
const https = await ensureTailscaleHttps(handle.port);
|
|
240
|
+
if (https.url) {
|
|
241
|
+
rows.push({
|
|
242
|
+
label: "Tailnet HTTPS",
|
|
243
|
+
value: c.green(c.bold(https.url)) + c.dim(" (secure context)"),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else if (https.reason) {
|
|
247
|
+
httpsWarning = https.reason;
|
|
248
|
+
}
|
|
198
249
|
}
|
|
199
|
-
console.log(
|
|
250
|
+
console.log(renderServeBanner(readVersion(), rows, httpsWarning));
|
|
200
251
|
// Fires only now that the server is actually listening (handle.url is a
|
|
201
252
|
// live, bound address) — a caller opening a browser tab any earlier races
|
|
202
253
|
// the server's own startup and shows connection errors until a manual
|