@repo-os/repoos 0.5.45
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.md +110 -0
- package/README.md +272 -0
- package/dist/.build-info.json +4 -0
- package/dist/.build-stamp.json +3 -0
- package/dist/cli/colors.d.ts +13 -0
- package/dist/cli/colors.js +37 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +221 -0
- package/dist/commands/check.d.ts +170 -0
- package/dist/commands/check.js +1227 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.js +70 -0
- package/dist/commands/gc.d.ts +1 -0
- package/dist/commands/gc.js +57 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +699 -0
- package/dist/commands/screenshots.d.ts +7 -0
- package/dist/commands/screenshots.js +77 -0
- package/dist/commands/serve.d.ts +55 -0
- package/dist/commands/serve.js +204 -0
- package/dist/commands/status.d.ts +140 -0
- package/dist/commands/status.js +569 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/tasks.d.ts +47 -0
- package/dist/commands/tasks.js +334 -0
- package/dist/commands/tunnel.d.ts +1 -0
- package/dist/commands/tunnel.js +1271 -0
- package/dist/commands/ui-harness.d.ts +53 -0
- package/dist/commands/ui-harness.js +62 -0
- package/dist/commands/ui-smoke.d.ts +6 -0
- package/dist/commands/ui-smoke.js +220 -0
- package/dist/commands/upgrade.d.ts +15 -0
- package/dist/commands/upgrade.js +188 -0
- package/dist/core/auth-store.d.ts +81 -0
- package/dist/core/auth-store.js +530 -0
- package/dist/core/auth.d.ts +75 -0
- package/dist/core/auth.js +124 -0
- package/dist/core/bootstrap.d.ts +41 -0
- package/dist/core/bootstrap.js +236 -0
- package/dist/core/build.d.ts +92 -0
- package/dist/core/build.js +249 -0
- package/dist/core/canary.d.ts +10 -0
- package/dist/core/canary.js +14 -0
- package/dist/core/check-failure-summary.d.ts +10 -0
- package/dist/core/check-failure-summary.js +34 -0
- package/dist/core/config.d.ts +160 -0
- package/dist/core/config.js +1288 -0
- package/dist/core/context-pack.d.ts +74 -0
- package/dist/core/context-pack.js +778 -0
- package/dist/core/db.d.ts +236 -0
- package/dist/core/db.js +747 -0
- package/dist/core/detect.d.ts +69 -0
- package/dist/core/detect.js +251 -0
- package/dist/core/docs.d.ts +76 -0
- package/dist/core/docs.js +225 -0
- package/dist/core/frontmatter.d.ts +29 -0
- package/dist/core/frontmatter.js +215 -0
- package/dist/core/git.d.ts +505 -0
- package/dist/core/git.js +1453 -0
- package/dist/core/indexer.d.ts +33 -0
- package/dist/core/indexer.js +282 -0
- package/dist/core/input.d.ts +66 -0
- package/dist/core/input.js +222 -0
- package/dist/core/logger.d.ts +70 -0
- package/dist/core/logger.js +138 -0
- package/dist/core/models.d.ts +45 -0
- package/dist/core/models.js +291 -0
- package/dist/core/net-probe.d.ts +4 -0
- package/dist/core/net-probe.js +20 -0
- package/dist/core/providers/deepinfra.d.ts +23 -0
- package/dist/core/providers/deepinfra.js +44 -0
- package/dist/core/providers/index.d.ts +24 -0
- package/dist/core/providers/index.js +59 -0
- package/dist/core/providers/openrouter.d.ts +28 -0
- package/dist/core/providers/openrouter.js +82 -0
- package/dist/core/providers/spend.d.ts +86 -0
- package/dist/core/providers/spend.js +286 -0
- package/dist/core/providers/types.d.ts +56 -0
- package/dist/core/providers/types.js +39 -0
- package/dist/core/repoos.d.ts +59 -0
- package/dist/core/repoos.js +221 -0
- package/dist/core/runtime.d.ts +35 -0
- package/dist/core/runtime.js +167 -0
- package/dist/core/skills-registry.d.ts +39 -0
- package/dist/core/skills-registry.js +148 -0
- package/dist/core/skills-registry.test.d.ts +1 -0
- package/dist/core/skills-registry.test.js +52 -0
- package/dist/core/sysmem.d.ts +7 -0
- package/dist/core/sysmem.js +55 -0
- package/dist/core/tailscale.d.ts +2 -0
- package/dist/core/tailscale.js +19 -0
- package/dist/core/task.d.ts +53 -0
- package/dist/core/task.js +310 -0
- package/dist/core/toml-line.d.ts +20 -0
- package/dist/core/toml-line.js +52 -0
- package/dist/core/toml-validate.d.ts +44 -0
- package/dist/core/toml-validate.js +335 -0
- package/dist/core/tunnel-assistant.d.ts +30 -0
- package/dist/core/tunnel-assistant.js +65 -0
- package/dist/core/tunnel-registry.d.ts +96 -0
- package/dist/core/tunnel-registry.js +267 -0
- package/dist/core/tunnel-registry.test.d.ts +1 -0
- package/dist/core/tunnel-registry.test.js +309 -0
- package/dist/core/tunnel.d.ts +103 -0
- package/dist/core/tunnel.js +320 -0
- package/dist/core/types.d.ts +811 -0
- package/dist/core/types.js +27 -0
- package/dist/core/worktree-gc.d.ts +57 -0
- package/dist/core/worktree-gc.js +194 -0
- package/dist/server/__tests__/integration-job.test.d.ts +1 -0
- package/dist/server/__tests__/integration-job.test.js +129 -0
- package/dist/server/agents.d.ts +1020 -0
- package/dist/server/agents.js +4070 -0
- package/dist/server/attachments.d.ts +51 -0
- package/dist/server/attachments.js +130 -0
- package/dist/server/auto-engineering.d.ts +45 -0
- package/dist/server/auto-engineering.js +275 -0
- package/dist/server/branchless-release.d.ts +22 -0
- package/dist/server/branchless-release.js +67 -0
- package/dist/server/built-in-agents.d.ts +305 -0
- package/dist/server/built-in-agents.js +1838 -0
- package/dist/server/cto-monitor.d.ts +35 -0
- package/dist/server/cto-monitor.js +281 -0
- package/dist/server/cto.d.ts +125 -0
- package/dist/server/cto.js +590 -0
- package/dist/server/deployments.d.ts +119 -0
- package/dist/server/deployments.js +325 -0
- package/dist/server/diff-snapshot.d.ts +20 -0
- package/dist/server/diff-snapshot.js +73 -0
- package/dist/server/done.d.ts +160 -0
- package/dist/server/done.js +488 -0
- package/dist/server/freeform.d.ts +55 -0
- package/dist/server/freeform.js +179 -0
- package/dist/server/handoff.d.ts +99 -0
- package/dist/server/handoff.js +575 -0
- package/dist/server/hetzner.d.ts +45 -0
- package/dist/server/hetzner.js +117 -0
- package/dist/server/icons.d.ts +8 -0
- package/dist/server/icons.js +195 -0
- package/dist/server/integration-job.d.ts +75 -0
- package/dist/server/integration-job.js +144 -0
- package/dist/server/integration-orchestrator.d.ts +211 -0
- package/dist/server/integration-orchestrator.js +1299 -0
- package/dist/server/integration-status.d.ts +48 -0
- package/dist/server/integration-status.js +71 -0
- package/dist/server/live-index.d.ts +295 -0
- package/dist/server/live-index.js +452 -0
- package/dist/server/model-test.d.ts +20 -0
- package/dist/server/model-test.js +100 -0
- package/dist/server/ntfy.d.ts +57 -0
- package/dist/server/ntfy.js +112 -0
- package/dist/server/pm-attachments.d.ts +35 -0
- package/dist/server/pm-attachments.js +181 -0
- package/dist/server/pm-runs.d.ts +58 -0
- package/dist/server/pm-runs.js +80 -0
- package/dist/server/preview.d.ts +174 -0
- package/dist/server/preview.js +641 -0
- package/dist/server/release.d.ts +88 -0
- package/dist/server/release.js +449 -0
- package/dist/server/reload.d.ts +194 -0
- package/dist/server/reload.js +586 -0
- package/dist/server/remote-validation.d.ts +132 -0
- package/dist/server/remote-validation.js +478 -0
- package/dist/server/repo-lock.d.ts +36 -0
- package/dist/server/repo-lock.js +141 -0
- package/dist/server/review-guard.d.ts +14 -0
- package/dist/server/review-guard.js +159 -0
- package/dist/server/review.d.ts +243 -0
- package/dist/server/review.js +1211 -0
- package/dist/server/routes/agents.d.ts +6 -0
- package/dist/server/routes/agents.js +28 -0
- package/dist/server/routes/auth.d.ts +60 -0
- package/dist/server/routes/auth.js +755 -0
- package/dist/server/routes/config.d.ts +20 -0
- package/dist/server/routes/config.js +347 -0
- package/dist/server/routes/debugger.d.ts +33 -0
- package/dist/server/routes/debugger.js +360 -0
- package/dist/server/routes/deployments.d.ts +3 -0
- package/dist/server/routes/deployments.js +16 -0
- package/dist/server/routes/docs.d.ts +8 -0
- package/dist/server/routes/docs.js +135 -0
- package/dist/server/routes/helpers.d.ts +21 -0
- package/dist/server/routes/helpers.js +171 -0
- package/dist/server/routes/index.d.ts +19 -0
- package/dist/server/routes/index.js +18 -0
- package/dist/server/routes/info.d.ts +17 -0
- package/dist/server/routes/info.js +177 -0
- package/dist/server/routes/inputs.d.ts +7 -0
- package/dist/server/routes/inputs.js +125 -0
- package/dist/server/routes/model-providers.d.ts +27 -0
- package/dist/server/routes/model-providers.js +94 -0
- package/dist/server/routes/models.d.ts +3 -0
- package/dist/server/routes/models.js +36 -0
- package/dist/server/routes/notify.d.ts +2 -0
- package/dist/server/routes/notify.js +17 -0
- package/dist/server/routes/playground.d.ts +29 -0
- package/dist/server/routes/playground.js +81 -0
- package/dist/server/routes/release.d.ts +20 -0
- package/dist/server/routes/release.js +86 -0
- package/dist/server/routes/router.d.ts +12 -0
- package/dist/server/routes/router.js +32 -0
- package/dist/server/routes/skill-registry.d.ts +5 -0
- package/dist/server/routes/skill-registry.js +51 -0
- package/dist/server/routes/tasks.d.ts +58 -0
- package/dist/server/routes/tasks.js +1442 -0
- package/dist/server/routes/transcribe.d.ts +2 -0
- package/dist/server/routes/transcribe.js +124 -0
- package/dist/server/routes/types.d.ts +68 -0
- package/dist/server/routes/types.js +1 -0
- package/dist/server/routes/ui.d.ts +5 -0
- package/dist/server/routes/ui.js +81 -0
- package/dist/server/routes/utils.d.ts +3 -0
- package/dist/server/routes/utils.js +23 -0
- package/dist/server/serve-reaper.d.ts +135 -0
- package/dist/server/serve-reaper.js +491 -0
- package/dist/server/server.d.ts +49 -0
- package/dist/server/server.js +2171 -0
- package/dist/server/supervisor.d.ts +89 -0
- package/dist/server/supervisor.js +231 -0
- package/dist/server/system.d.ts +198 -0
- package/dist/server/system.js +480 -0
- package/dist/server/task-check.d.ts +45 -0
- package/dist/server/task-check.js +71 -0
- package/dist/server/task-transitions.d.ts +51 -0
- package/dist/server/task-transitions.js +78 -0
- package/dist/server/task-watchdog.d.ts +158 -0
- package/dist/server/task-watchdog.js +681 -0
- package/dist/server/test-run.d.ts +28 -0
- package/dist/server/test-run.js +81 -0
- package/dist/server/watcher.d.ts +21 -0
- package/dist/server/watcher.js +194 -0
- package/dist/server/write.d.ts +94 -0
- package/dist/server/write.js +345 -0
- package/dist/ui/assets/AgentsView-CF4wRvHh.css +1 -0
- package/dist/ui/assets/AgentsView-D-myL-kA.js +1 -0
- package/dist/ui/assets/ContextView-D3TC-RSp.css +1 -0
- package/dist/ui/assets/ContextView-DsOqAdyE.js +4 -0
- package/dist/ui/assets/DashboardView-BOhsIJqO.js +1 -0
- package/dist/ui/assets/DashboardView-Ck9o0RP2.css +1 -0
- package/dist/ui/assets/DeploymentsView--iIUVfEH.css +1 -0
- package/dist/ui/assets/DeploymentsView-DXu_FYR2.js +1 -0
- package/dist/ui/assets/InputsView-24HVbVPk.js +1 -0
- package/dist/ui/assets/InputsView-BLb7GHtx.css +1 -0
- package/dist/ui/assets/LoginView-6QlGg6cx.css +1 -0
- package/dist/ui/assets/LoginView-BqupI0Lt.js +1 -0
- package/dist/ui/assets/ReleasesView-BEG1A0YH.css +1 -0
- package/dist/ui/assets/ReleasesView-CAgMuxpe.js +3 -0
- package/dist/ui/assets/SettingsView-CA-NYSMK.css +1 -0
- package/dist/ui/assets/SettingsView-O3iZSnjg.js +3 -0
- package/dist/ui/assets/WorkView-D8Tl6qwy.js +1 -0
- package/dist/ui/assets/WorkView-DMYL2s3W.css +1 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-J5lkSnEE.js +1 -0
- package/dist/ui/assets/arc-BHZnmhhM.js +1 -0
- package/dist/ui/assets/architecture-7GRP2DOG-pLdFt-xS.js +1 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-C3k89uKX.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-AWNoBzop.js +129 -0
- package/dist/ui/assets/button-lbikqSph.js +1 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-DKG1D5gd.js +38 -0
- package/dist/ui/assets/channel-gA4qBv3A.js +1 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/assets/chunk-4HAMMTFA-D1YR8lF_.js +62 -0
- package/dist/ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/assets/chunk-75Z2AOVW-5GuBxfsL.js +2 -0
- package/dist/ui/assets/chunk-DU6HZSFF-DfdNNqwa.js +127 -0
- package/dist/ui/assets/chunk-F27PBJKO-BPZzinQ4.js +1 -0
- package/dist/ui/assets/chunk-FOHPRMQF-l2z4K2jD.js +161 -0
- package/dist/ui/assets/chunk-GMAD6QVW-DGWvSKj5.js +72 -0
- package/dist/ui/assets/chunk-GVQU2GXP-D6Aw75wJ.js +1 -0
- package/dist/ui/assets/chunk-IMKFNOWR-B8mMBKCW.js +231 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/assets/chunk-L3NEJ4N5-D5OJuXYE.js +1 -0
- package/dist/ui/assets/chunk-OSK3NFVY-B7r9w2UW.js +10 -0
- package/dist/ui/assets/chunk-P2QGCYS3-C74X4AQX.js +1 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-C5_u1gUS.js +1 -0
- package/dist/ui/assets/chunk-PWAF6VOD-DfoTLtxn.js +1 -0
- package/dist/ui/assets/chunk-SHT3W25Y-D40m2eqz.js +168 -0
- package/dist/ui/assets/chunk-SVP7TREG-euxi0Kap.js +88 -0
- package/dist/ui/assets/chunk-TICWLB2K-fkdxngzN.js +206 -0
- package/dist/ui/assets/chunk-XXDRQBXY-DFBUG-OT.js +1 -0
- package/dist/ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-BzW8bq2P.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-BzW8bq2P.js +1 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-BTcgMon1.js +1 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-B5m_COsS.js +1 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-C-AG6CGe.js +62 -0
- package/dist/ui/assets/cytoscape.esm-FBntH-9t.js +321 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DDfDnp8L.js +4 -0
- package/dist/ui/assets/dagre-PrKaheQc.js +1 -0
- package/dist/ui/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-DjQZAlEY.js +30 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-DAZ9yp1B.js +41 -0
- package/dist/ui/assets/diagram-VSXAHHWV-BWepUKJD.js +3 -0
- package/dist/ui/assets/diagram-VX7I27RA-EcV1J81b.js +24 -0
- package/dist/ui/assets/diagram-Z3DM3KII-Dbz7XU_K.js +24 -0
- package/dist/ui/assets/dist-DTg6UBE_.js +1 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-BUyQw8Mm.js +1 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-WVf8ukKt.js +99 -0
- package/dist/ui/assets/eventmodeling-NTZA5JFV-DHqyLOAn.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-CYk_ze5K.js +1 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-rB4MZuB-.js +292 -0
- package/dist/ui/assets/gitGraph-4MIJSDKK-BOTWM8dY.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-eYscuhtP.js +106 -0
- package/dist/ui/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/assets/index-BACBfnga.css +1 -0
- package/dist/ui/assets/index-BOQ-ust6.js +26 -0
- package/dist/ui/assets/info-A6RAGUB7-_67wOORe.js +1 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-XVXbddTA.js +2 -0
- package/dist/ui/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-ClLNiEru.js +70 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-NRawjR6i.js +139 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-BgSzBfIx.js +89 -0
- package/dist/ui/assets/katex-DolUETbr.js +257 -0
- package/dist/ui/assets/line-KAyfuul4.js +1 -0
- package/dist/ui/assets/linear-cCCPOpu7.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-CgXZk1iN.js +7 -0
- package/dist/ui/assets/mermaid.core-BUUkChHW.js +44 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-Cpn7vW4r.js +96 -0
- package/dist/ui/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/assets/packet-AYTQ26CC-0lZwM-d6.js +1 -0
- package/dist/ui/assets/path-COt_16Va.js +1 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-impy0inn.js +1 -0
- package/dist/ui/assets/pie-WAS4IAKB-CqfdGHYg.js +1 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-CxjL4I1h.js +39 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-Cjd1QMIe.js +7 -0
- package/dist/ui/assets/radar-RG4KPBEZ-CRkQIwiv.js +1 -0
- package/dist/ui/assets/railroad-74A4TZTK-i67XSWTB.js +1 -0
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-Cv9m5aS3.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-C5ZIn-Jh.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-CGra9Oq-.js +1 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-_pK0qyaY.js +1 -0
- package/dist/ui/assets/releases-B0WNV-SY.js +1 -0
- package/dist/ui/assets/repoos-cto-square.webp +0 -0
- package/dist/ui/assets/repoos-helpful-pm-square.webp +0 -0
- package/dist/ui/assets/repoos-orchestrator-square.webp +0 -0
- package/dist/ui/assets/repoos-performance-agent-square.webp +0 -0
- package/dist/ui/assets/repoos-ross-from-friends-square.webp +0 -0
- package/dist/ui/assets/repoos-tech-debt-agent-square.webp +0 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-CRrFX-yw.js +84 -0
- package/dist/ui/assets/rough.esm-By172zw-.js +1 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-X_xFRj9E.js +40 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-Byb49lKf.js +162 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/dist/ui/assets/src-BH-TyZbA.js +1 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CzaPrqYF.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BCXckz7K.js +1 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Cx_1lnqr.js +1 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-54qsKi6A.js +8 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-B1IbZPQ-.js +120 -0
- package/dist/ui/assets/treeView-Q6P3EWNA-DFWhVE3d.js +1 -0
- package/dist/ui/assets/treemap-WGGIJYW6-BmbYPThn.js +1 -0
- package/dist/ui/assets/vendor-ui-DPm7zLxi.js +3 -0
- package/dist/ui/assets/vendor-vue-CobgyEJp.js +1 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-Ae2AtRpt.js +34 -0
- package/dist/ui/assets/wardley-WFR3VGLG-BtD4X4tL.js +1 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-B3YN2fTd.js +78 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-CIzJfMKA.js +7 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/index.html +49 -0
- package/dist/ui/sw.js +48 -0
- package/package.json +91 -0
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `repoos init` — make any repo RepoOS-ready, idempotently. Safe to re-run: it
|
|
3
|
+
* never overwrites existing files, only creates what's missing.
|
|
4
|
+
*
|
|
5
|
+
* In a directory that is NOT inside a git repo, `repoos init` switches to a
|
|
6
|
+
* guided, interactive flow that creates a brand-new RepoOS project from
|
|
7
|
+
* scratch: optionally in a subdirectory, with a choice of layout (repo root
|
|
8
|
+
* vs. a `repoos/` subfolder, config file at root either way), an optional
|
|
9
|
+
* one-line project description (seeded into the sample task) and an optional
|
|
10
|
+
* initial commit.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
13
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
|
|
14
|
+
import { createServer } from "node:net";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { createInterface } from "node:readline/promises";
|
|
17
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
18
|
+
import { deriveServePort, findRepoRoot, loadConfig } from "../core/config.js";
|
|
19
|
+
import { gitAvailable, gitCommitAll, gitConfig, gitInit, isGitRepo } from "../core/git.js";
|
|
20
|
+
import { c } from "../cli/colors.js";
|
|
21
|
+
import { cmdServe } from "./serve.js";
|
|
22
|
+
const SAMPLE_TASK = (description) => `---
|
|
23
|
+
id: "0001"
|
|
24
|
+
title: Set up RepoOS
|
|
25
|
+
type: chore
|
|
26
|
+
status: done
|
|
27
|
+
priority: p2
|
|
28
|
+
area: infra
|
|
29
|
+
assigned_to: unassigned
|
|
30
|
+
created_by: human
|
|
31
|
+
branch: ""
|
|
32
|
+
---
|
|
33
|
+
${description ? `\n## Overview\n\n${description}\n` : ""}
|
|
34
|
+
## Problem
|
|
35
|
+
|
|
36
|
+
The repo needed a lightweight, repo-native way to track work that AI agents
|
|
37
|
+
and humans share. Tasks live as markdown files, versioned in git.
|
|
38
|
+
|
|
39
|
+
## Desired UX
|
|
40
|
+
|
|
41
|
+
Run \`repoos list\` to see the board. Run \`repoos show 0001\` to read a task
|
|
42
|
+
(this one). Agents read these files directly for full context.
|
|
43
|
+
|
|
44
|
+
## Acceptance criteria
|
|
45
|
+
|
|
46
|
+
- [x] \`repoos init\` scaffolded work/, repoos.toml, AGENTS.md
|
|
47
|
+
- [x] \`repoos list\` shows this task
|
|
48
|
+
- [x] Editing the \`status:\` field moves it across the board
|
|
49
|
+
|
|
50
|
+
This task exists to show you the shape of a task file — it's done because
|
|
51
|
+
running \`repoos init\` already satisfies everything above. There's nothing to
|
|
52
|
+
"work" here; move on to your next task.
|
|
53
|
+
|
|
54
|
+
## Notes for AI
|
|
55
|
+
|
|
56
|
+
Status is a frontmatter field — never move files between folders. Keep diffs
|
|
57
|
+
small. Read AGENTS.md before starting any task.
|
|
58
|
+
`;
|
|
59
|
+
/**
|
|
60
|
+
* The first *workable* task a guided new-project init leaves on the board.
|
|
61
|
+
* 0001 is deliberately `done` (scaffolding is the whole of it), so this is what
|
|
62
|
+
* a new user — or their first agent — actually picks up. `repoos init` runs
|
|
63
|
+
* before any server or agent exists, so the body itself is the prompt: it
|
|
64
|
+
* embeds the one-line description collected at init and carries the questions
|
|
65
|
+
* that turn that description into docs and real tasks later.
|
|
66
|
+
*/
|
|
67
|
+
const NEW_PROJECT_STARTER_TASK = (id, description) => `---
|
|
68
|
+
id: "${id}"
|
|
69
|
+
title: Flesh out the product vision and initial architecture
|
|
70
|
+
type: spec
|
|
71
|
+
status: ready
|
|
72
|
+
priority: p2
|
|
73
|
+
area: product
|
|
74
|
+
assigned_to: unassigned
|
|
75
|
+
created_by: human
|
|
76
|
+
branch: ""
|
|
77
|
+
---
|
|
78
|
+
## Overview
|
|
79
|
+
|
|
80
|
+
This project started from a one-line description. This task turns it into
|
|
81
|
+
something a team can actually build from — a shared vision, an initial
|
|
82
|
+
architecture, and a first batch of concrete work.
|
|
83
|
+
|
|
84
|
+
## Project description
|
|
85
|
+
|
|
86
|
+
${description
|
|
87
|
+
? `> ${description}`
|
|
88
|
+
: "_No description was given at init time. Start by writing one line that says what this project is and who it's for._"}
|
|
89
|
+
|
|
90
|
+
## What to do
|
|
91
|
+
|
|
92
|
+
1. Read the description above and the rest of this repo.
|
|
93
|
+
2. Work through the questions that block design — on your own, or with the
|
|
94
|
+
project's owner:
|
|
95
|
+
- Who is this for, and what is the smallest useful first release?
|
|
96
|
+
- What stack and hosting, and why those over the alternatives?
|
|
97
|
+
- What is explicitly **out of scope** for now?
|
|
98
|
+
3. Write the answers into \`docs/\` — at minimum a short vision note and an
|
|
99
|
+
architecture note. Keep them specific to this project.
|
|
100
|
+
4. Break the result into a handful of concrete tasks with
|
|
101
|
+
\`repoos new "<title>"\`, and move the ones that are ready into \`ready\`.
|
|
102
|
+
5. Record the docs you wrote here, then move this task to \`review\` (or
|
|
103
|
+
\`done\` if there is genuinely nothing left to capture).
|
|
104
|
+
|
|
105
|
+
## Notes for AI
|
|
106
|
+
|
|
107
|
+
This task is self-contained: the description above and the questions in step 2
|
|
108
|
+
are the prompt. Ask the human before inventing answers about stack, scope or
|
|
109
|
+
priorities — a short back-and-forth here saves a lot of rework later.
|
|
110
|
+
`;
|
|
111
|
+
/**
|
|
112
|
+
* The first workable task for `repoos init` inside an existing codebase. There
|
|
113
|
+
* is already a repo to read, so unlike the new-project starter this one is
|
|
114
|
+
* about documenting what exists and turning the gaps into a real backlog.
|
|
115
|
+
*/
|
|
116
|
+
const EXISTING_REPO_STARTER_TASK = (id) => `---
|
|
117
|
+
id: "${id}"
|
|
118
|
+
title: Read this codebase and propose docs/ + an initial task backlog
|
|
119
|
+
type: spec
|
|
120
|
+
status: ready
|
|
121
|
+
priority: p2
|
|
122
|
+
area: docs
|
|
123
|
+
assigned_to: unassigned
|
|
124
|
+
created_by: human
|
|
125
|
+
branch: ""
|
|
126
|
+
---
|
|
127
|
+
## Overview
|
|
128
|
+
|
|
129
|
+
RepoOS was just added to an existing codebase. The first useful move is to read
|
|
130
|
+
what's already here, write down what a newcomer — human or agent — would need,
|
|
131
|
+
then turn what's missing into work.
|
|
132
|
+
|
|
133
|
+
## What to do
|
|
134
|
+
|
|
135
|
+
1. Scan the repo: top-level structure, languages and frameworks, build and
|
|
136
|
+
test commands, and any existing conventions or docs.
|
|
137
|
+
2. Write the durable findings into \`docs/\` — architecture notes and the
|
|
138
|
+
conventions anyone working here must follow.
|
|
139
|
+
3. Draft a small starter backlog of real tasks with \`repoos new "<title>"\`,
|
|
140
|
+
each concrete enough to work on its own.
|
|
141
|
+
4. Record what you wrote here, then move this task to \`review\` (or \`done\` if
|
|
142
|
+
there is nothing left to capture).
|
|
143
|
+
|
|
144
|
+
## Notes for AI
|
|
145
|
+
|
|
146
|
+
Read \`AGENTS.md\` first — it's the operating loop for this repo. Base the docs
|
|
147
|
+
on what the code actually does rather than what you'd expect it to; where the
|
|
148
|
+
repo is silent, write down the open question instead of guessing.
|
|
149
|
+
`;
|
|
150
|
+
const AGENTS_MD = `# AGENTS.md
|
|
151
|
+
|
|
152
|
+
This repo uses **RepoOS**: tasks are markdown files under \`work/\`, and the
|
|
153
|
+
repo itself is the source of truth. This file tells AI agents how to operate.
|
|
154
|
+
|
|
155
|
+
## Operating loop
|
|
156
|
+
|
|
157
|
+
1. Read this file and any relevant docs under \`docs/\`.
|
|
158
|
+
2. Pick a task from \`work/\` whose \`status: ready\`.
|
|
159
|
+
3. Set its \`status: active\` (edit the frontmatter; do not move the file).
|
|
160
|
+
4. Create a worktree on the branch named in the task's \`branch:\` field, or set one.
|
|
161
|
+
5. Implement → test → if the repo has a git remote, open an MR/PR against main.
|
|
162
|
+
6. Set \`status: review\` when ready for human sign-off. **Leave the worktree open; do NOT merge its branch.**
|
|
163
|
+
|
|
164
|
+
## Review and sign-off
|
|
165
|
+
|
|
166
|
+
A \`review\` task stays in its open worktree until a human (or another AI) signs
|
|
167
|
+
off; the implementer never merges to \`main\` at \`review\` time.
|
|
168
|
+
|
|
169
|
+
- No git remote: on approval, the reviewer says **"move task <id> to done"**. The
|
|
170
|
+
implementer then sets \`status: done\` (commit \`docs(<id>): set status done\`),
|
|
171
|
+
fast-forward merges the branch to \`main\`, removes the worktree, and deletes
|
|
172
|
+
the branch.
|
|
173
|
+
- With a git remote: an MR/PR is opened against \`main\` at \`review\` time. On
|
|
174
|
+
approval it is merged by the reviewer (or by the implementer only on "move task
|
|
175
|
+
<id> to done"), then remote + local branches are deleted and \`status\` is set
|
|
176
|
+
to \`done\`.
|
|
177
|
+
- Requested changes are fixed on the same worktree, tests re-run, and the task
|
|
178
|
+
re-set to \`review\`.
|
|
179
|
+
- If the \`reviewer\` agent is enabled on the Agents page, RepoOS reviews the
|
|
180
|
+
task automatically when it lands in \`review\` and shows a short report (bugs,
|
|
181
|
+
edge cases, suggestions) in the task drawer. It is advisory: the human still
|
|
182
|
+
reviews and is the only one who moves a task to \`done\`.
|
|
183
|
+
|
|
184
|
+
## Rules
|
|
185
|
+
|
|
186
|
+
- **Never** move task files between folders. Status lives in frontmatter.
|
|
187
|
+
- **Never write directly to \`work/*.md\` files.** All task creation and
|
|
188
|
+
manipulation goes through \`repoos\` commands or HTTP API endpoints
|
|
189
|
+
(\`POST /api/tasks\`, \`PATCH /api/tasks/:id\`, etc.). If the RepoOS server
|
|
190
|
+
is unreachable, stop and report the issue — do not hand-write task files.
|
|
191
|
+
- **The Product Manager agent is authorized to create and update tasks.** It
|
|
192
|
+
must use those same RepoOS CLI commands or HTTP API endpoints for task body,
|
|
193
|
+
metadata, and status changes; it must never edit task Markdown directly.
|
|
194
|
+
- **Never** deploy to production without human sign-off.
|
|
195
|
+
- Keep frontmatter tidy; \`repoos\` will normalize key order on write.
|
|
196
|
+
- One task = one focused worktree.
|
|
197
|
+
|
|
198
|
+
## Conventions
|
|
199
|
+
|
|
200
|
+
Document stack-specific conventions here (framework, lint, test commands).
|
|
201
|
+
|
|
202
|
+
## UI previews are server-owned
|
|
203
|
+
|
|
204
|
+
Never run \`repoos serve\` yourself and never pick a port — RepoOS owns the
|
|
205
|
+
control-plane port and every preview port, and direct serve attempts from agent
|
|
206
|
+
processes are rejected. Preview requests are the human's to make — do NOT
|
|
207
|
+
automatically request a preview before handoff or as a routine part of finishing
|
|
208
|
+
a task. If the human explicitly asks you to verify a change the way a browser
|
|
209
|
+
would see it, request this task's managed preview by emitting the exact signal
|
|
210
|
+
line (idempotent, and no localhost or curl is required — your sandbox may have
|
|
211
|
+
no network access to the control plane):
|
|
212
|
+
|
|
213
|
+
::repoos-preview-request::
|
|
214
|
+
|
|
215
|
+
RepoOS validates the request against your live run, starts the preview from
|
|
216
|
+
your worktree, probes it server-side, and records the preview URL and probe
|
|
217
|
+
result in your task transcript. The preview is reaped when the task leaves
|
|
218
|
+
active/review.
|
|
219
|
+
`;
|
|
220
|
+
function repoosToml(layout) {
|
|
221
|
+
const ns = layout === "repoos"
|
|
222
|
+
? `workDir = "repoos/work"\ndocsDir = "repoos/docs"\ncacheDir = "repoos/.repoos"\n`
|
|
223
|
+
: "";
|
|
224
|
+
return `# RepoOS configuration. All fields optional — these are the defaults.
|
|
225
|
+
|
|
226
|
+
${ns}defaultStatus = "inbox"
|
|
227
|
+
defaultAssignee = "unassigned"
|
|
228
|
+
|
|
229
|
+
# Optional features, off by default — uncomment to turn them on. Real
|
|
230
|
+
# secrets (API keys, client secrets) go in .env, never here (this file is
|
|
231
|
+
# git-tracked); see .env.example for the matching variables.
|
|
232
|
+
|
|
233
|
+
# [auth] # require login (email OTP / Google) — docs/native-auth.md
|
|
234
|
+
# enabled = true
|
|
235
|
+
# bootstrapAdmin = "you@example.com" # only this email can claim the first admin account
|
|
236
|
+
#
|
|
237
|
+
# [auth.emailProvider]
|
|
238
|
+
# type = "resend"
|
|
239
|
+
# fromAddress = "noreply@yourdomain.com"
|
|
240
|
+
#
|
|
241
|
+
# [auth.google] # optional — adds a "Sign in with Google" button
|
|
242
|
+
# clientId = "..." # clientSecret goes in .env, not here
|
|
243
|
+
|
|
244
|
+
# [whisper] # voice-to-text in text areas
|
|
245
|
+
# provider = "groq" # or "openai"
|
|
246
|
+
|
|
247
|
+
# ntfyEnabled = true # push notifications on task lifecycle events
|
|
248
|
+
# ntfyTopic = "repoos_myproject"
|
|
249
|
+
# ntfyBaseUrl = "https://ntfy.sh" # or your self-hosted ntfy server
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
const ENV_EXAMPLE = `# Copy to .env and fill in what you need — .env is gitignored, this file is
|
|
253
|
+
# tracked so the repo documents which secrets a full setup expects.
|
|
254
|
+
# repoos serve auto-loads .env at startup; real shell/process-supervisor env
|
|
255
|
+
# vars still take precedence over it.
|
|
256
|
+
|
|
257
|
+
# --- Auth (docs/native-auth.md) — only used when [auth].enabled = true ---
|
|
258
|
+
# REPOOS_RESEND_API_KEY=re_...
|
|
259
|
+
# REPOOS_GOOGLE_CLIENT_SECRET=...
|
|
260
|
+
# REPOOS_AUTH_SESSION_SECRET=... # auto-generated on first boot if omitted
|
|
261
|
+
|
|
262
|
+
# --- Voice-to-text transcription (only used when [whisper] is configured) ---
|
|
263
|
+
# REPOOS_WHISPER_KEY=... # or GROQ_API_KEY / OPENAI_API_KEY directly
|
|
264
|
+
|
|
265
|
+
# --- repoos tunnel (Cloudflare Tunnel + Access) ---
|
|
266
|
+
# CLOUDFLARE_API_TOKEN=...
|
|
267
|
+
|
|
268
|
+
# --- ntfy.sh push notifications ---
|
|
269
|
+
# NTFY_BASE_URL=https://ntfy.sh # or your self-hosted ntfy server
|
|
270
|
+
`;
|
|
271
|
+
const INITIAL_COMMIT_MSG = "chore: initialize RepoOS project";
|
|
272
|
+
const STARTER_TASK = {
|
|
273
|
+
new: { slug: "flesh-out-the-vision", build: NEW_PROJECT_STARTER_TASK },
|
|
274
|
+
existing: { slug: "read-the-codebase", build: (id) => EXISTING_REPO_STARTER_TASK(id) },
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Next free 4-digit task id for a scaffolded file, derived from the ids that
|
|
278
|
+
* already exist under `workDir` (matching the numbering scheme used by
|
|
279
|
+
* `createTask`). 0001 is written just before this runs, so a fresh scaffold
|
|
280
|
+
* gets 0002; a repo that already has tasks gets the next id past the highest.
|
|
281
|
+
*/
|
|
282
|
+
function nextScaffoldId(root, workDir) {
|
|
283
|
+
let max = 0;
|
|
284
|
+
try {
|
|
285
|
+
for (const name of readdirSync(join(root, workDir))) {
|
|
286
|
+
const m = name.match(/^(\d+)/);
|
|
287
|
+
if (m)
|
|
288
|
+
max = Math.max(max, parseInt(m[1], 10));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
/* workDir doesn't exist yet — start from zero */
|
|
293
|
+
}
|
|
294
|
+
return String(max + 1).padStart(4, "0");
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Locate an already-seeded starter task by its slug suffix. Init is
|
|
298
|
+
* idempotent, so once the starter exists a re-run must not write another one —
|
|
299
|
+
* and crucially, must not compute a *new* id every time (which would append a
|
|
300
|
+
* fresh 0002, 0003, … starter on each run).
|
|
301
|
+
*/
|
|
302
|
+
function findStarter(root, workDir, slug) {
|
|
303
|
+
try {
|
|
304
|
+
const match = readdirSync(join(root, workDir)).find((n) => n.endsWith(`-${slug}.md`));
|
|
305
|
+
return match ? join(workDir, match).split("\\").join("/") : null;
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
export function scaffoldInto(root, description, layout = "root", kind = "new") {
|
|
312
|
+
const created = [];
|
|
313
|
+
const skipped = [];
|
|
314
|
+
const ensureDir = (rel) => {
|
|
315
|
+
const p = join(root, rel);
|
|
316
|
+
if (!existsSync(p)) {
|
|
317
|
+
mkdirSync(p, { recursive: true });
|
|
318
|
+
created.push(rel + "/");
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
skipped.push(rel + "/");
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const ensureFile = (rel, content) => {
|
|
325
|
+
const p = join(root, rel);
|
|
326
|
+
if (!existsSync(p)) {
|
|
327
|
+
writeFileSync(p, content);
|
|
328
|
+
created.push(rel);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
skipped.push(rel);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
// Write config first so workDir/docsDir/cacheDir overrides are in effect
|
|
335
|
+
// before the dirs are created (namespace layout) — or so an existing
|
|
336
|
+
// config is respected untouched (existing-repo path).
|
|
337
|
+
ensureFile("repoos.toml", repoosToml(layout));
|
|
338
|
+
const config = loadConfig(root);
|
|
339
|
+
ensureDir(config.workDir);
|
|
340
|
+
ensureDir(config.docsDir);
|
|
341
|
+
ensureFile("AGENTS.md", AGENTS_MD);
|
|
342
|
+
ensureFile(join(config.workDir, "0001-set-up-repoos.md"), SAMPLE_TASK(description));
|
|
343
|
+
// 0001 is `done` (scaffolding is all of it), so without this the ready
|
|
344
|
+
// column is empty right after init. Seed one genuinely workable task; its
|
|
345
|
+
// id follows whatever is already on the board.
|
|
346
|
+
const starter = STARTER_TASK[kind];
|
|
347
|
+
const existingStarter = findStarter(root, config.workDir, starter.slug);
|
|
348
|
+
if (existingStarter) {
|
|
349
|
+
skipped.push(existingStarter);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const starterId = nextScaffoldId(root, config.workDir);
|
|
353
|
+
ensureFile(join(config.workDir, `${starterId}-${starter.slug}.md`), starter.build(starterId, description));
|
|
354
|
+
}
|
|
355
|
+
ensureFile(".env.example", ENV_EXAMPLE);
|
|
356
|
+
// gitignore the derived cache and local secrets
|
|
357
|
+
const giPath = join(root, ".gitignore");
|
|
358
|
+
const ignoreLines = [
|
|
359
|
+
{ comment: "# RepoOS derived index cache", line: `${config.cacheDir}/` },
|
|
360
|
+
{ comment: "# Local secrets — see .env.example", line: ".env" },
|
|
361
|
+
];
|
|
362
|
+
const existingLines = existsSync(giPath) ? readFileSync(giPath, "utf8").split(/\r?\n/) : [];
|
|
363
|
+
const toAdd = ignoreLines.filter((l) => !existingLines.some((e) => e.trim() === l.line));
|
|
364
|
+
if (toAdd.length === 0) {
|
|
365
|
+
skipped.push(".gitignore");
|
|
366
|
+
}
|
|
367
|
+
else if (existsSync(giPath)) {
|
|
368
|
+
appendFileSync(giPath, "\n" + toAdd.map((l) => `${l.comment}\n${l.line}`).join("\n\n") + "\n");
|
|
369
|
+
created.push(".gitignore (+entry)");
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
writeFileSync(giPath, toAdd.map((l) => `${l.comment}\n${l.line}`).join("\n\n") + "\n");
|
|
373
|
+
created.push(".gitignore");
|
|
374
|
+
}
|
|
375
|
+
return { created, skipped };
|
|
376
|
+
}
|
|
377
|
+
function reportInit(root, created, skipped) {
|
|
378
|
+
console.log(c.bold(c.cyan("\n RepoOS initialized")) + c.dim(` · ${root}\n`));
|
|
379
|
+
for (const f of created)
|
|
380
|
+
console.log(" " + c.green("created ") + f);
|
|
381
|
+
for (const f of skipped)
|
|
382
|
+
console.log(" " + c.dim("exists " + f));
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* The marker that identifies a RepoOS project root: repoos.toml. It lives at
|
|
386
|
+
* the root in BOTH layouts (root and repoos/ namespace), so this alone is a
|
|
387
|
+
* reliable, unambiguous signal. We deliberately do NOT scan for work/ dirs —
|
|
388
|
+
* a `repoos/work/` folder could just be the actual repoos repo clone (or any
|
|
389
|
+
* unrelated `work/`), which would make sibling directories false-positive as
|
|
390
|
+
* "already a RepoOS project".
|
|
391
|
+
*/
|
|
392
|
+
function repoOSMarker(dir) {
|
|
393
|
+
const p = join(dir, "repoos.toml");
|
|
394
|
+
return existsSync(p) ? p : null;
|
|
395
|
+
}
|
|
396
|
+
/** Nearest ancestor (or the dir itself) that already has a RepoOS layout. */
|
|
397
|
+
function findRepoOSDir(start) {
|
|
398
|
+
let dir = resolve(start);
|
|
399
|
+
while (true) {
|
|
400
|
+
if (repoOSMarker(dir))
|
|
401
|
+
return dir;
|
|
402
|
+
const parent = resolve(dir, "..");
|
|
403
|
+
if (parent === dir)
|
|
404
|
+
return null;
|
|
405
|
+
dir = parent;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function warnAlreadySetUp(dir, hint) {
|
|
409
|
+
console.log(c.yellow("\n RepoOS is already set up in ") + c.cyan(dir) + c.yellow("."));
|
|
410
|
+
if (hint)
|
|
411
|
+
console.log(c.dim(` ${hint}`));
|
|
412
|
+
const marker = repoOSMarker(dir);
|
|
413
|
+
if (marker) {
|
|
414
|
+
console.log(c.dim(" Detected because ") + c.cyan(marker) + c.dim(" exists."));
|
|
415
|
+
}
|
|
416
|
+
console.log(c.dim(" To initialize a brand-new project, run repoos init in a different (empty) directory."));
|
|
417
|
+
}
|
|
418
|
+
async function ask(question) {
|
|
419
|
+
const rl = createInterface({ input, output });
|
|
420
|
+
try {
|
|
421
|
+
const answer = await rl.question(question);
|
|
422
|
+
return answer.trim();
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
rl.close();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async function confirm(question, dflt) {
|
|
429
|
+
const hint = dflt ? " [Y/n]" : " [y/N]";
|
|
430
|
+
const answer = (await ask(question + c.dim(hint) + " ")).toLowerCase();
|
|
431
|
+
if (answer === "")
|
|
432
|
+
return dflt;
|
|
433
|
+
return answer === "y" || answer === "yes";
|
|
434
|
+
}
|
|
435
|
+
/** Ask where the scaffold should live: repo root (default) or a repoos/ subfolder. */
|
|
436
|
+
async function askLayout() {
|
|
437
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
438
|
+
const answer = (await ask(" Where should RepoOS live?" +
|
|
439
|
+
c.dim(" r = repo root (default), n = repoos/ subfolder — avoids clashing with existing dirs") +
|
|
440
|
+
": ")).toLowerCase();
|
|
441
|
+
if (answer === "" || answer === "r" || answer === "root")
|
|
442
|
+
return "root";
|
|
443
|
+
if (answer === "n" || answer === "namespace" || answer === "repoos")
|
|
444
|
+
return "repoos";
|
|
445
|
+
console.log(c.yellow(` "${answer}" — type r (root) or n (repoos/ subfolder).`));
|
|
446
|
+
}
|
|
447
|
+
return "root";
|
|
448
|
+
}
|
|
449
|
+
/** Open a URL in the default browser. Fail-soft (best effort, never blocks). */
|
|
450
|
+
function openBrowser(url) {
|
|
451
|
+
const cmd = process.platform === "darwin"
|
|
452
|
+
? ["open", url]
|
|
453
|
+
: process.platform === "win32"
|
|
454
|
+
? ["cmd", "/c", "start", "", url]
|
|
455
|
+
: ["xdg-open", url];
|
|
456
|
+
try {
|
|
457
|
+
const child = spawn(cmd[0], cmd.slice(1), {
|
|
458
|
+
detached: true,
|
|
459
|
+
stdio: "ignore",
|
|
460
|
+
});
|
|
461
|
+
child.on("error", () => { });
|
|
462
|
+
child.unref();
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
/* fail-soft */
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function probePort(port) {
|
|
469
|
+
return new Promise((resolve) => {
|
|
470
|
+
const srv = createServer();
|
|
471
|
+
srv.once("error", () => resolve(false));
|
|
472
|
+
srv.listen(port, "127.0.0.1", () => srv.close(() => resolve(true)));
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
/** First free port at or above `start` (bounded). Falls back to `start`. */
|
|
476
|
+
async function nextFreePort(start) {
|
|
477
|
+
for (let p = start; p < start + 50; p++) {
|
|
478
|
+
if (await probePort(p))
|
|
479
|
+
return p;
|
|
480
|
+
}
|
|
481
|
+
return start;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Ask for a serve port, defaulting to this repo's stable derived port (the same
|
|
485
|
+
* one `repoos serve` would pick). `explicit` is true only when the user typed a
|
|
486
|
+
* concrete port — the caller then pins it in repoos.toml so future `serve`s
|
|
487
|
+
* reuse it.
|
|
488
|
+
*/
|
|
489
|
+
async function askPort(derived) {
|
|
490
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
491
|
+
const raw = await ask(" Preferred port" +
|
|
492
|
+
c.dim(` [${derived}] — Enter for this repo's default, 0 = let the OS pick a free port`) +
|
|
493
|
+
": ");
|
|
494
|
+
if (raw === "")
|
|
495
|
+
return { port: derived, explicit: false };
|
|
496
|
+
const n = Number(raw);
|
|
497
|
+
if (Number.isInteger(n) && n >= 0 && n <= 65535) {
|
|
498
|
+
return { port: n, explicit: n > 0 };
|
|
499
|
+
}
|
|
500
|
+
console.log(c.yellow(` "${raw}" isn't a valid port.`));
|
|
501
|
+
}
|
|
502
|
+
return { port: derived, explicit: false };
|
|
503
|
+
}
|
|
504
|
+
/** Pin `servePort` in repoos.toml so future `repoos serve` runs reuse it. */
|
|
505
|
+
function persistServePort(root, port) {
|
|
506
|
+
const tomlPath = join(root, "repoos.toml");
|
|
507
|
+
try {
|
|
508
|
+
const text = readFileSync(tomlPath, "utf8");
|
|
509
|
+
if (/^\s*servePort\s*=/m.test(text))
|
|
510
|
+
return;
|
|
511
|
+
const line = `servePort = ${port}\n`;
|
|
512
|
+
const anchor = /^defaultAssignee\s*=.*$/m;
|
|
513
|
+
const next = anchor.test(text)
|
|
514
|
+
? text.replace(anchor, (m) => `${m}\nservePort = ${port}`)
|
|
515
|
+
: text + (text.endsWith("\n") ? "" : "\n") + line;
|
|
516
|
+
writeFileSync(tomlPath, next);
|
|
517
|
+
console.log(" " + c.green("saved") + c.dim(` servePort = ${port} to repoos.toml`));
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
/* fail-soft — the server still starts on the chosen port this run */
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async function guidedNewRepo(args) {
|
|
524
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
525
|
+
console.error(c.red("\n This directory isn't a git repo, so repoos init needs interactive prompts."));
|
|
526
|
+
console.error(c.dim(" Run it in a terminal, or run `git init` first and then `repoos init` again."));
|
|
527
|
+
process.exitCode = 1;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const cwd = process.cwd();
|
|
531
|
+
let projectName = (args[0] ?? "").trim();
|
|
532
|
+
if (projectName && !/^[A-Za-z0-9._-]+$/.test(projectName)) {
|
|
533
|
+
console.error(c.red(` Invalid project name "${projectName}" — use letters, digits, . _ -`));
|
|
534
|
+
process.exitCode = 1;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
console.log(c.dim("\n Not inside a git repository."));
|
|
538
|
+
console.log(c.cyan(" repoos init will create a new RepoOS project here."));
|
|
539
|
+
console.log(c.dim(" Default: the CURRENT directory. Enter a project name to use a subdirectory instead."));
|
|
540
|
+
if (!projectName) {
|
|
541
|
+
projectName = await ask(" Project name" + c.dim(" (Enter = current directory)") + ": ");
|
|
542
|
+
}
|
|
543
|
+
let target = cwd;
|
|
544
|
+
if (projectName) {
|
|
545
|
+
target = join(cwd, projectName);
|
|
546
|
+
if (repoOSMarker(target)) {
|
|
547
|
+
warnAlreadySetUp(target, "Nothing to create — that project already exists.");
|
|
548
|
+
process.exitCode = 1;
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
console.log();
|
|
552
|
+
const ok = await confirm(" Create the project in a new subdirectory " +
|
|
553
|
+
c.cyan(`./${projectName}`) +
|
|
554
|
+
c.dim(` → ${target}`), true);
|
|
555
|
+
if (!ok) {
|
|
556
|
+
console.log(c.yellow("\n Cancelled — nothing was created."));
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
console.log(c.dim(` → Using the current directory: ${cwd}`));
|
|
562
|
+
}
|
|
563
|
+
const layout = await askLayout();
|
|
564
|
+
console.log();
|
|
565
|
+
const scaffoldFiles = layout === "repoos"
|
|
566
|
+
? "repoos/work/, repoos/docs/, AGENTS.md, repoos.toml, .gitignore"
|
|
567
|
+
: "work/, docs/, AGENTS.md, repoos.toml, .gitignore";
|
|
568
|
+
const proceed = await confirm(" Ready to " +
|
|
569
|
+
c.cyan("git init") +
|
|
570
|
+
c.dim(" and scaffold " + scaffoldFiles) +
|
|
571
|
+
" in " +
|
|
572
|
+
c.cyan(target), true);
|
|
573
|
+
if (!proceed) {
|
|
574
|
+
console.log(c.yellow("\n Cancelled — nothing was created."));
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const description = await ask(" Project description" +
|
|
578
|
+
c.dim(" — one line, gives the AI context to suggest next steps (optional, Enter to skip)") +
|
|
579
|
+
": ");
|
|
580
|
+
if (!existsSync(target))
|
|
581
|
+
mkdirSync(target, { recursive: true });
|
|
582
|
+
// git health warnings — fail-soft, scaffold regardless
|
|
583
|
+
const gitOk = gitAvailable(target);
|
|
584
|
+
if (!gitOk) {
|
|
585
|
+
console.log(c.yellow("\n Warning: git doesn't appear to be installed — scaffolding without git."));
|
|
586
|
+
}
|
|
587
|
+
else if (!gitConfig(target, "user.name") && !gitConfig(target, "user.email")) {
|
|
588
|
+
console.log(c.yellow("\n Warning: no git identity is configured."));
|
|
589
|
+
console.log(c.dim(" git may auto-detect it, or the initial commit may fail. Set it with:"));
|
|
590
|
+
console.log(c.dim(' git config --global user.name "You" && git config --global user.email you@example.com'));
|
|
591
|
+
}
|
|
592
|
+
const { created, skipped } = scaffoldInto(target, description, layout);
|
|
593
|
+
reportInit(target, created, skipped);
|
|
594
|
+
if (!gitOk) {
|
|
595
|
+
console.log(c.dim(" To add git later: git init && git add -A && git commit"));
|
|
596
|
+
}
|
|
597
|
+
else if (gitInit(target)) {
|
|
598
|
+
console.log(" " + c.green("git init") + c.dim(" ok"));
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
console.log(c.yellow(" Warning: git init failed — files left uncommitted."));
|
|
602
|
+
}
|
|
603
|
+
if (gitOk && (await confirm("\n Make an initial commit of the scaffold?", true))) {
|
|
604
|
+
const hash = gitCommitAll(target, INITIAL_COMMIT_MSG);
|
|
605
|
+
if (hash) {
|
|
606
|
+
console.log(" " + c.green("committed ") + c.dim(hash));
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
console.log(c.yellow(" Warning: initial commit failed (unconfigured identity or a hook) — files left uncommitted."));
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (await confirm("\n Launch the RepoOS web console now to start building?", true)) {
|
|
613
|
+
const { port: preferred, explicit } = await askPort(deriveServePort(target));
|
|
614
|
+
let port = preferred;
|
|
615
|
+
if (preferred > 0) {
|
|
616
|
+
const free = await nextFreePort(preferred);
|
|
617
|
+
if (free !== preferred) {
|
|
618
|
+
console.log(c.yellow(` Port ${preferred} is in use — using ${free} instead.`));
|
|
619
|
+
port = free;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Only pin an explicitly chosen port. The derived default is already stable
|
|
623
|
+
// per checkout, so writing it would just add noise to repoos.toml.
|
|
624
|
+
if (explicit)
|
|
625
|
+
persistServePort(target, preferred);
|
|
626
|
+
const dirHint = target === cwd ? null : `cd ${target}`;
|
|
627
|
+
if (target !== cwd)
|
|
628
|
+
process.chdir(target);
|
|
629
|
+
// A child process can change ITS OWN cwd (above) but never the parent
|
|
630
|
+
// shell's — the user's terminal is still sitting in `cwd`. Print the
|
|
631
|
+
// exact commands to run rather than just noting the mismatch, so there's
|
|
632
|
+
// nothing to figure out once this server is stopped.
|
|
633
|
+
if (dirHint) {
|
|
634
|
+
console.log(c.dim("\n Your shell is still in ") +
|
|
635
|
+
c.cyan(cwd) +
|
|
636
|
+
c.dim(". When you come back to this project, run:\n\n") +
|
|
637
|
+
" " +
|
|
638
|
+
c.cyan(dirHint) +
|
|
639
|
+
"\n " +
|
|
640
|
+
c.cyan("repoos serve") +
|
|
641
|
+
"\n");
|
|
642
|
+
}
|
|
643
|
+
console.log(c.dim(" Starting the RepoOS web console…"));
|
|
644
|
+
// Opening the browser is deferred to onReady — the server isn't actually
|
|
645
|
+
// listening until cmdServe's startServer() resolves, and opening a tab
|
|
646
|
+
// any earlier races that startup, showing connection errors until a
|
|
647
|
+
// manual reload.
|
|
648
|
+
await cmdServe(["--port", String(port)], {
|
|
649
|
+
onReady: (url) => openBrowser(url),
|
|
650
|
+
onShutdown: dirHint
|
|
651
|
+
? () => console.log(c.dim(" Start it again with:\n\n ") +
|
|
652
|
+
c.cyan(dirHint) +
|
|
653
|
+
"\n " +
|
|
654
|
+
c.cyan("repoos serve") +
|
|
655
|
+
"\n")
|
|
656
|
+
: undefined,
|
|
657
|
+
});
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const dirHint = target === cwd ? "" : `cd ${target} · `;
|
|
661
|
+
console.log("\n Next: " +
|
|
662
|
+
c.cyan(dirHint + "repoos list") +
|
|
663
|
+
c.dim(" · ") +
|
|
664
|
+
c.cyan("repoos show 0001") +
|
|
665
|
+
c.dim(" · ") +
|
|
666
|
+
c.cyan('repoos new "My task"') +
|
|
667
|
+
c.dim(" · ") +
|
|
668
|
+
c.cyan("repoos serve") +
|
|
669
|
+
c.dim(" to open the web console") +
|
|
670
|
+
"\n");
|
|
671
|
+
}
|
|
672
|
+
export async function cmdInit(args) {
|
|
673
|
+
const cwd = process.cwd();
|
|
674
|
+
if (isGitRepo(cwd)) {
|
|
675
|
+
// existing-repo path — unchanged, idempotent, no prompts
|
|
676
|
+
const root = findRepoRoot(cwd);
|
|
677
|
+
const { created, skipped } = scaffoldInto(root, "", "root", "existing");
|
|
678
|
+
if (created.length === 0) {
|
|
679
|
+
warnAlreadySetUp(root, "Nothing to initialize here.");
|
|
680
|
+
for (const f of skipped)
|
|
681
|
+
console.log(" " + c.dim("exists " + f));
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
reportInit(root, created, skipped);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const alreadyHere = findRepoOSDir(cwd);
|
|
688
|
+
if (alreadyHere) {
|
|
689
|
+
warnAlreadySetUp(alreadyHere, "This directory isn't a git repo, but it sits inside an existing RepoOS setup.");
|
|
690
|
+
process.exitCode = 1;
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
await guidedNewRepo(args);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
console.log(c.yellow("\n Cancelled."));
|
|
698
|
+
}
|
|
699
|
+
}
|