@glrs-dev/cli 0.0.1 → 0.1.1
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/CHANGELOG.md +50 -0
- package/README.md +14 -15
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-6RHN2EDH.js +93 -0
- package/dist/chunk-DEODG2LC.js +55 -0
- package/dist/chunk-FSAGM22T.js +17 -0
- package/dist/chunk-GQBZREK5.js +136 -0
- package/dist/chunk-HWMRY35D.js +139 -0
- package/dist/chunk-LMRDQ4GW.js +129 -0
- package/dist/chunk-NLPX2KOF.js +149 -0
- package/dist/chunk-P7PRH4I3.js +177 -0
- package/dist/chunk-VCN7RNLU.js +60 -0
- package/dist/chunk-VJFNIKQJ.js +120 -0
- package/dist/chunk-W37UX3U2.js +35 -0
- package/dist/chunk-YBCA3IP6.js +25 -0
- package/dist/chunk-YGNDPKIW.js +99 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +89 -36
- package/dist/commands/cleanup.d.ts +19 -0
- package/dist/commands/cleanup.js +11 -0
- package/dist/commands/create.d.ts +17 -0
- package/dist/commands/create.js +12 -0
- package/dist/commands/delete.d.ts +17 -0
- package/dist/commands/delete.js +12 -0
- package/dist/commands/go.d.ts +4 -0
- package/dist/commands/go.js +11 -0
- package/dist/commands/list.d.ts +15 -0
- package/dist/commands/list.js +12 -0
- package/dist/commands/switch.d.ts +11 -0
- package/dist/commands/switch.js +12 -0
- package/dist/commands/types.d.ts +10 -0
- package/dist/commands/types.js +0 -0
- package/dist/index.d.ts +16 -19
- package/dist/index.js +4 -1
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +14 -0
- package/dist/lib/fmt.d.ts +12 -0
- package/dist/lib/fmt.js +25 -0
- package/dist/lib/git.d.ts +26 -0
- package/dist/lib/git.js +25 -0
- package/dist/lib/registry.d.ts +14 -0
- package/dist/lib/registry.js +13 -0
- package/dist/lib/select.d.ts +21 -0
- package/dist/lib/select.js +10 -0
- package/dist/lib/worktree.d.ts +35 -0
- package/dist/lib/worktree.js +17 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/agents-md-writer.md +89 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/architecture-advisor.md +46 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/build.md +93 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/code-searcher.md +54 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/docs-maintainer.md +128 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/gap-analyzer.md +44 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/lib-reader.md +39 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.md +107 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-planner.md +153 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +49 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +144 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +374 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-reviewer.md +68 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-thorough.md +63 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/research.md +138 -0
- package/dist/vendor/harness-opencode/dist/agents/shared/index.ts +26 -0
- package/dist/vendor/harness-opencode/dist/agents/shared/workflow-mechanics.md +32 -0
- package/dist/vendor/harness-opencode/dist/bin/memory-mcp-launcher.sh +145 -0
- package/dist/vendor/harness-opencode/dist/bin/plan-check.sh +255 -0
- package/dist/vendor/harness-opencode/dist/chunk-VJUETC6A.js +205 -0
- package/dist/vendor/harness-opencode/dist/chunk-VVMP6QWS.js +731 -0
- package/dist/vendor/harness-opencode/dist/chunk-XCZ3NOXR.js +703 -0
- package/dist/vendor/harness-opencode/dist/cli.d.ts +1 -0
- package/dist/vendor/harness-opencode/dist/cli.js +5096 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/autopilot.md +96 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/costs.md +94 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/fresh.md +382 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/init-deep.md +196 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/research.md +27 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/review.md +96 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/ship.md +104 -0
- package/dist/vendor/harness-opencode/dist/index.d.ts +21 -0
- package/dist/vendor/harness-opencode/dist/index.js +2092 -0
- package/dist/vendor/harness-opencode/dist/install-4EYR56OR.js +9 -0
- package/dist/vendor/harness-opencode/dist/skills/agent-estimation/SKILL.md +159 -0
- package/dist/vendor/harness-opencode/dist/skills/paths.ts +18 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/SKILL.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/dag-shape.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/decomposition.md +36 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/first-principles.md +29 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/milestones.md +57 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/self-review.md +46 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/task-context.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/touches-scope.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/verify-design.md +53 -0
- package/dist/vendor/harness-opencode/dist/skills/research/SKILL.md +350 -0
- package/dist/vendor/harness-opencode/dist/skills/research-auto/SKILL.md +283 -0
- package/dist/vendor/harness-opencode/dist/skills/research-local/SKILL.md +268 -0
- package/dist/vendor/harness-opencode/dist/skills/research-web/SKILL.md +119 -0
- package/dist/vendor/harness-opencode/dist/skills/review-plan/SKILL.md +32 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/README.md +60 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/AGENTS.md +2975 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/README.md +123 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/SKILL.md +137 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/web-design-guidelines/SKILL.md +39 -0
- package/dist/vendor/harness-opencode/package.json +11 -0
- package/package.json +20 -15
- package/LICENSE +0 -21
- package/dist/chunk-TU23AE2F.js +0 -69
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @glrs-dev/cli
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`050f4b9`](https://github.com/iceglober/glrs/commit/050f4b9bf2304dd5fb5031c38e7fe247b68ead07) Thanks [@iceglober](https://github.com/iceglober)! - **Rename `@glrs-dev/harness-opencode` → `@glrs-dev/harness-plugin-opencode` and republish.**
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
OpenCode resolves plugins by npm-installing them into `~/.cache/opencode/packages/<plugin>@<version>/` at plugin-load time. The previous plan — marking `@glrs-dev/harness-opencode` as `private: true` and vendoring it only into `@glrs-dev/cli` — broke OpenCode's plugin loader because the package wasn't published on npm, causing `ETARGET: No matching version found for @glrs-dev/harness-opencode@1.0.0`.
|
|
12
|
+
|
|
13
|
+
The fix: publish the plugin under a new name (`@glrs-dev/harness-plugin-opencode`) so OpenCode can resolve it normally. The old name stays deprecated at its last published version (`0.16.2`).
|
|
14
|
+
|
|
15
|
+
## What changed
|
|
16
|
+
|
|
17
|
+
- `packages/harness-opencode/package.json`: renamed from `@glrs-dev/harness-opencode` to `@glrs-dev/harness-plugin-opencode`, `private: true` removed, `publishConfig.access: public` + `provenance: true` added, version reset to `0.1.0` (fresh name on npm).
|
|
18
|
+
- The `install` / `uninstall` / `doctor` flows now write the new name to `opencode.json`'s plugin array.
|
|
19
|
+
- `@glrs-dev/cli` still bundles a vendored copy of the plugin for standalone subprocess dispatch (`glrs oc`), but the npm-resolved copy is what OpenCode's plugin runtime loads.
|
|
20
|
+
- Bin names unchanged — `harness-opencode` and `glrs-oc` still work.
|
|
21
|
+
|
|
22
|
+
## Migration for existing users
|
|
23
|
+
|
|
24
|
+
Re-run `glrs oc install` to update your `opencode.json` plugin array from `@glrs-dev/harness-opencode` to `@glrs-dev/harness-plugin-opencode`. The old entry will be replaced; no data loss.
|
|
25
|
+
|
|
26
|
+
## 0.1.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- [`37da38d`](https://github.com/iceglober/glrs/commit/37da38d0c0ffebf6758ef696644be3c79203eb4d) Thanks [@iceglober](https://github.com/iceglober)! - **First release of `@glrs-dev/cli` as the unified entry point for the @glrs-dev ecosystem.**
|
|
31
|
+
|
|
32
|
+
## What's included
|
|
33
|
+
|
|
34
|
+
- `glrs oc <args>` — dispatches to a vendored copy of `@glrs-dev/harness-opencode` (the OpenCode agent harness). The harness-opencode bin is bundled inside this tarball at `dist/vendor/harness-opencode/`; no separate install needed.
|
|
35
|
+
- `glrs wt <args>` — worktree management (create, list, switch, delete, cleanup). Stores worktrees under `~/.glorious/worktrees/<repo>/<name>/`.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm i -g @glrs-dev/cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Requires Bun >= 1.2.0 on PATH — the CLI and the vendored harness use Bun-native APIs (`bun:sqlite`, `Bun.spawn`).
|
|
44
|
+
|
|
45
|
+
## Migration
|
|
46
|
+
|
|
47
|
+
- `@glrs-dev/harness-opencode` on npm is being deprecated. Its final published version (`0.16.2` or later) should be the last. Users should migrate to `glrs oc <args>`.
|
|
48
|
+
- `@glrs-dev/agentic` has been removed from the repo. Its worktree-management commands live natively under `glrs wt`.
|
|
49
|
+
- `@glrs-dev/assume` remains a separate, standalone package — install it independently if you need the SSO credential manager.
|
|
50
|
+
|
|
51
|
+
- [`b3ff224`](https://github.com/iceglober/glrs/commit/b3ff2249da36abb6669588ddf08d57c2d3b00464) Thanks [@iceglober](https://github.com/iceglober)! - Add `@research` agent and four bundled research skills (`research`, `research-web`, `research-local`, `research-auto`) to the vendored harness-opencode. `@research` is an Opus-class, `mode: all` orchestrator that decomposes research queries into parallel workstreams, dispatches per-workstream sub-agents using one of the four skills (multi-round umbrella, local codebase, web, or autonomous `.lab/` experimentation), reviews findings for gaps, iterates, and synthesizes. The existing `/research` slash command is rewritten as a thin delegator to `@research`; PRIME's subagent-reference recap gains an `@research` entry so its task-tool picker surfaces the agent alongside `@plan`, `@build`, `@code-searcher`, `@qa-reviewer`, etc.
|
|
52
|
+
|
|
3
53
|
## 0.0.1
|
|
4
54
|
|
|
5
55
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
# `@glrs-dev/cli`
|
|
2
2
|
|
|
3
|
-
Unified CLI for the [@glrs-dev](https://www.npmjs.com/org/glrs-dev) ecosystem. One binary,
|
|
3
|
+
Unified CLI for the [@glrs-dev](https://www.npmjs.com/org/glrs-dev) ecosystem. One binary, two subcommands:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm i -g @glrs-dev/cli
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
glrs oc install
|
|
11
|
-
glrs
|
|
12
|
-
glrs
|
|
10
|
+
glrs oc install # → OpenCode harness install
|
|
11
|
+
glrs wt new feature # create a worktree
|
|
12
|
+
glrs wt list # list worktrees across repos
|
|
13
|
+
glrs wt switch # interactive picker
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Requires [Bun](https://bun.sh) on PATH at runtime.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
The `harness-opencode` bin remains available directly for power users who prefer the untagged entry point.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
## How it works
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
2. Resolves the underlying binary via `require.resolve(<package>/package.json)` → reads the `bin` field
|
|
23
|
-
3. Spawns the binary with the remaining argv forwarded, inheriting stdio
|
|
24
|
-
4. Exits with the child's exit code
|
|
22
|
+
The `glrs` binary has two subcommands:
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
- **`glrs oc <args>`** — dispatches to [`@glrs-dev/harness-plugin-opencode`](../harness-opencode/) (bundled as a dependency). Resolves the bin via `require.resolve(<package>/package.json)` → reads the `bin` field → spawns with argv forwarded.
|
|
25
|
+
- **`glrs wt <args>`** — worktree management, handled natively. Commands: `new`, `list`, `switch`, `delete`, `cleanup`. Worktrees are stored in `~/.glorious/worktrees/<repo>/<name>/`.
|
|
27
26
|
|
|
28
27
|
## Philosophy
|
|
29
28
|
|
|
30
|
-
- **Don't duplicate CLI logic.**
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
29
|
+
- **Don't duplicate CLI logic.** `glrs oc` is a thin spawn wrapper around `harness-opencode`.
|
|
30
|
+
- **One install, one thing to remember** for the harness + worktree workflow.
|
|
31
|
+
- **Separate concerns stay separate.** The SSO credential tool [`@glrs-dev/assume`](../assume/) is a standalone Rust binary installed separately.
|
|
33
32
|
|
|
34
33
|
## Docs
|
|
35
34
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
go
|
|
3
|
+
} from "./chunk-DEODG2LC.js";
|
|
4
|
+
import {
|
|
5
|
+
currentBranchIn,
|
|
6
|
+
gitInSafe,
|
|
7
|
+
gitRoot,
|
|
8
|
+
gitSafe,
|
|
9
|
+
listWorktrees
|
|
10
|
+
} from "./chunk-LMRDQ4GW.js";
|
|
11
|
+
import {
|
|
12
|
+
loadRegistry
|
|
13
|
+
} from "./chunk-VCN7RNLU.js";
|
|
14
|
+
import {
|
|
15
|
+
bold,
|
|
16
|
+
dim
|
|
17
|
+
} from "./chunk-YBCA3IP6.js";
|
|
18
|
+
|
|
19
|
+
// src/commands/list.ts
|
|
20
|
+
import * as path from "path";
|
|
21
|
+
import { command, flag } from "cmd-ts";
|
|
22
|
+
var list = command({
|
|
23
|
+
name: "list",
|
|
24
|
+
aliases: ["ls"],
|
|
25
|
+
description: "List all worktrees across repos",
|
|
26
|
+
args: {
|
|
27
|
+
interactive: flag({
|
|
28
|
+
long: "interactive",
|
|
29
|
+
short: "i",
|
|
30
|
+
description: "Interactively pick a worktree to switch into (same as `wt switch`)."
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
handler: async ({ interactive }) => {
|
|
34
|
+
if (interactive) {
|
|
35
|
+
await go();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const entries = loadRegistry();
|
|
39
|
+
if (entries.length === 0) {
|
|
40
|
+
if (gitSafe("rev-parse", "--git-dir")) {
|
|
41
|
+
showLocalWorktrees();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log(
|
|
45
|
+
dim("No worktrees registered. Create one with: glrs wt new")
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const list2 = byRepo.get(entry.repo) ?? [];
|
|
52
|
+
list2.push(entry);
|
|
53
|
+
byRepo.set(entry.repo, list2);
|
|
54
|
+
}
|
|
55
|
+
for (const [repo, repoEntries] of byRepo) {
|
|
56
|
+
console.log(`
|
|
57
|
+
${bold(repo)} ${dim(repoEntries[0].repoPath)}`);
|
|
58
|
+
for (const entry of repoEntries) {
|
|
59
|
+
const current = currentBranchIn(entry.wtPath);
|
|
60
|
+
const branchCol = current && current !== entry.branch ? `${entry.branch} ${dim(`\u2192 ${current}`)}` : current ?? entry.branch;
|
|
61
|
+
const commit = gitInSafe(entry.wtPath, "rev-parse", "--short", "HEAD") ?? dim("?");
|
|
62
|
+
console.log(
|
|
63
|
+
` ${pad(branchCol, 45)} ${pad(commit, 10)} ${dim(entry.wtPath)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
function showLocalWorktrees() {
|
|
71
|
+
const entries = listWorktrees();
|
|
72
|
+
const root = gitRoot();
|
|
73
|
+
console.log(bold(pad("NAME", 40) + pad("BRANCH", 30) + "COMMIT"));
|
|
74
|
+
console.log(pad("\u2500\u2500\u2500\u2500", 40) + pad("\u2500\u2500\u2500\u2500\u2500\u2500", 30) + "\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
let displayName = path.basename(entry.path);
|
|
77
|
+
if (entry.path === root) {
|
|
78
|
+
displayName += ` ${dim("(main)")}`;
|
|
79
|
+
}
|
|
80
|
+
const branch = entry.branch ? entry.branch.replace("refs/heads/", "") : "(detached)";
|
|
81
|
+
const shortCommit = entry.commit.slice(0, 7);
|
|
82
|
+
console.log(pad(displayName, 40) + pad(branch, 30) + shortCommit);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function pad(s, width) {
|
|
86
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
87
|
+
const padding = Math.max(0, width - visible.length);
|
|
88
|
+
return s + " ".repeat(padding);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
list
|
|
93
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
select
|
|
3
|
+
} from "./chunk-P7PRH4I3.js";
|
|
4
|
+
import {
|
|
5
|
+
currentBranchIn,
|
|
6
|
+
spawnShell
|
|
7
|
+
} from "./chunk-LMRDQ4GW.js";
|
|
8
|
+
import {
|
|
9
|
+
loadRegistry
|
|
10
|
+
} from "./chunk-VCN7RNLU.js";
|
|
11
|
+
import {
|
|
12
|
+
bold,
|
|
13
|
+
dim,
|
|
14
|
+
info,
|
|
15
|
+
warn
|
|
16
|
+
} from "./chunk-YBCA3IP6.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/go.ts
|
|
19
|
+
async function go() {
|
|
20
|
+
const entries = loadRegistry();
|
|
21
|
+
if (entries.length === 0) {
|
|
22
|
+
warn(
|
|
23
|
+
"no worktrees registered \u2014 create one with: glrs wt new"
|
|
24
|
+
);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const list = byRepo.get(entry.repo) ?? [];
|
|
30
|
+
list.push(entry);
|
|
31
|
+
byRepo.set(entry.repo, list);
|
|
32
|
+
}
|
|
33
|
+
const groups = [];
|
|
34
|
+
for (const [repo, repoEntries] of byRepo) {
|
|
35
|
+
groups.push({
|
|
36
|
+
title: repo,
|
|
37
|
+
choices: repoEntries.map((e) => {
|
|
38
|
+
const current = currentBranchIn(e.wtPath);
|
|
39
|
+
const label = current && current !== e.branch ? `${e.branch} ${dim(`\u2192 ${current}`)}` : e.branch;
|
|
40
|
+
return { label, value: e, hint: e.wtPath };
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const selected = await select({
|
|
45
|
+
message: "Select a worktree",
|
|
46
|
+
groups
|
|
47
|
+
});
|
|
48
|
+
if (!selected) return;
|
|
49
|
+
info(`opening shell in ${bold(selected.branch)}...`);
|
|
50
|
+
spawnShell(selected.wtPath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
go
|
|
55
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
go
|
|
3
|
+
} from "./chunk-DEODG2LC.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/switch.ts
|
|
6
|
+
import { command } from "cmd-ts";
|
|
7
|
+
var switchCmd = command({
|
|
8
|
+
name: "switch",
|
|
9
|
+
aliases: ["sw"],
|
|
10
|
+
description: "Interactive worktree picker \u2014 select a worktree to open a shell in",
|
|
11
|
+
args: {},
|
|
12
|
+
handler: go
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
switchCmd
|
|
17
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createWorktree
|
|
3
|
+
} from "./chunk-VJFNIKQJ.js";
|
|
4
|
+
import {
|
|
5
|
+
spawnShell
|
|
6
|
+
} from "./chunk-LMRDQ4GW.js";
|
|
7
|
+
import {
|
|
8
|
+
loadRegistry
|
|
9
|
+
} from "./chunk-VCN7RNLU.js";
|
|
10
|
+
import {
|
|
11
|
+
info
|
|
12
|
+
} from "./chunk-YBCA3IP6.js";
|
|
13
|
+
import {
|
|
14
|
+
__require
|
|
15
|
+
} from "./chunk-3RG5ZIWI.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/create.ts
|
|
18
|
+
import { command, positional, option, optional, string } from "cmd-ts";
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
import * as os from "os";
|
|
21
|
+
var INDEX_DIR = path.join(os.homedir(), ".glorious");
|
|
22
|
+
var INDEX_FILE = path.join(INDEX_DIR, "repos.json");
|
|
23
|
+
function existsSync(filePath) {
|
|
24
|
+
return Bun.file(filePath).existsSync();
|
|
25
|
+
}
|
|
26
|
+
function readTextSync(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
return Bun.file(filePath).textSync();
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function loadRepoIndex() {
|
|
34
|
+
if (!existsSync(INDEX_FILE)) return {};
|
|
35
|
+
try {
|
|
36
|
+
const raw = readTextSync(INDEX_FILE);
|
|
37
|
+
if (!raw) return {};
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function lookupRepo(name) {
|
|
44
|
+
return loadRepoIndex()[name] ?? null;
|
|
45
|
+
}
|
|
46
|
+
function scanRoots() {
|
|
47
|
+
return ["~/repos", "~/code", "~/src"].map((p) => p.replace("~", os.homedir())).filter((p) => existsSync(p));
|
|
48
|
+
}
|
|
49
|
+
function findRepoByScan(name, maxDepth = 4) {
|
|
50
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", "target", "dist", "vendor"]);
|
|
51
|
+
for (const root of scanRoots()) {
|
|
52
|
+
const hit = walk(root, name, skip, maxDepth, 0);
|
|
53
|
+
if (hit) return hit;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function walk(dir, name, skip, maxDepth, depth) {
|
|
58
|
+
if (depth > maxDepth) return null;
|
|
59
|
+
if (path.basename(dir) === name && existsSync(path.join(dir, ".git"))) {
|
|
60
|
+
return dir;
|
|
61
|
+
}
|
|
62
|
+
let children;
|
|
63
|
+
try {
|
|
64
|
+
children = __require("fs").readdirSync(dir);
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (existsSync(path.join(dir, ".git"))) return null;
|
|
69
|
+
for (const child of children) {
|
|
70
|
+
if (skip.has(child) || child.startsWith(".")) continue;
|
|
71
|
+
const full = path.join(dir, child);
|
|
72
|
+
let stat;
|
|
73
|
+
try {
|
|
74
|
+
stat = __require("fs").statSync(full);
|
|
75
|
+
} catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!stat.isDirectory()) continue;
|
|
79
|
+
const found = walk(full, name, skip, maxDepth, depth + 1);
|
|
80
|
+
if (found) return found;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function resolveRepoPath(repo) {
|
|
85
|
+
if (!repo) return void 0;
|
|
86
|
+
const wt = loadRegistry().find((e) => e.repo === repo);
|
|
87
|
+
if (wt) {
|
|
88
|
+
if (!existsSync(wt.repoPath)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Registered repo '${repo}' points to ${wt.repoPath}, which no longer exists.`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return wt.repoPath;
|
|
94
|
+
}
|
|
95
|
+
const indexed = lookupRepo(repo);
|
|
96
|
+
if (indexed) return indexed;
|
|
97
|
+
const scanned = findRepoByScan(repo);
|
|
98
|
+
if (scanned) return scanned;
|
|
99
|
+
throw new Error(
|
|
100
|
+
`No repo named '${repo}' found.
|
|
101
|
+
Looked in worktree registry, ~/.glorious/repos.json, and scan roots.
|
|
102
|
+
Fix: run 'glrs wt new' from inside the repo once.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
var create = command({
|
|
106
|
+
name: "new",
|
|
107
|
+
aliases: ["create"],
|
|
108
|
+
description: "Create a worktree from the latest origin default branch. Name is auto-generated.",
|
|
109
|
+
args: {
|
|
110
|
+
repo: positional({
|
|
111
|
+
type: optional(string),
|
|
112
|
+
displayName: "repo",
|
|
113
|
+
description: "Optional repo name. Required when running outside a git repo; looked up in the worktree registry, the repo index, or under repo.scan-roots."
|
|
114
|
+
}),
|
|
115
|
+
from: option({
|
|
116
|
+
type: optional(string),
|
|
117
|
+
long: "from",
|
|
118
|
+
description: "Base branch override (default: remote default branch). Rare \u2014 prefer default."
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
handler: ({ repo, from }) => {
|
|
122
|
+
const repoPath = resolveRepoPath(repo);
|
|
123
|
+
const { wtPath } = createWorktree({ from, repoPath, repo });
|
|
124
|
+
console.log(`
|
|
125
|
+
cd ${wtPath}
|
|
126
|
+
`);
|
|
127
|
+
if (process.stdin.isTTY) {
|
|
128
|
+
info("spawning shell in worktree (exit to return)...");
|
|
129
|
+
spawnShell(wtPath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
export {
|
|
135
|
+
create
|
|
136
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isProtected
|
|
3
|
+
} from "./chunk-W37UX3U2.js";
|
|
4
|
+
import {
|
|
5
|
+
defaultBranch,
|
|
6
|
+
git,
|
|
7
|
+
gitInSafe,
|
|
8
|
+
gitRoot,
|
|
9
|
+
gitSafe,
|
|
10
|
+
listWorktrees
|
|
11
|
+
} from "./chunk-LMRDQ4GW.js";
|
|
12
|
+
import {
|
|
13
|
+
unregisterWorktree
|
|
14
|
+
} from "./chunk-VCN7RNLU.js";
|
|
15
|
+
import {
|
|
16
|
+
bold,
|
|
17
|
+
dim,
|
|
18
|
+
info,
|
|
19
|
+
ok,
|
|
20
|
+
red,
|
|
21
|
+
warn
|
|
22
|
+
} from "./chunk-YBCA3IP6.js";
|
|
23
|
+
|
|
24
|
+
// src/commands/cleanup.ts
|
|
25
|
+
import * as readline from "readline";
|
|
26
|
+
import { command, option, flag, optional, string } from "cmd-ts";
|
|
27
|
+
var cleanup = command({
|
|
28
|
+
name: "cleanup",
|
|
29
|
+
description: "Delete worktrees whose branches are merged or stale",
|
|
30
|
+
args: {
|
|
31
|
+
base: option({
|
|
32
|
+
type: optional(string),
|
|
33
|
+
long: "base",
|
|
34
|
+
description: "Base branch to check against (default: auto-detect)"
|
|
35
|
+
}),
|
|
36
|
+
dryRun: flag({
|
|
37
|
+
long: "dry-run",
|
|
38
|
+
description: "Show candidates without deleting"
|
|
39
|
+
}),
|
|
40
|
+
yes: flag({ long: "yes", short: "y", description: "Skip confirmation" })
|
|
41
|
+
},
|
|
42
|
+
handler: async ({ base: baseOpt, dryRun, yes }) => {
|
|
43
|
+
const base = baseOpt ?? defaultBranch();
|
|
44
|
+
info(`checking worktrees against ${bold(base)}...`);
|
|
45
|
+
gitSafe("fetch", "origin", base, "--quiet");
|
|
46
|
+
const entries = listWorktrees();
|
|
47
|
+
const root = gitRoot();
|
|
48
|
+
const candidates = [];
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (entry.path === root) continue;
|
|
51
|
+
const branch = entry.branch?.replace("refs/heads/", "");
|
|
52
|
+
if (!branch) continue;
|
|
53
|
+
if (isProtected(branch)) continue;
|
|
54
|
+
const merged = gitSafe(
|
|
55
|
+
"merge-base",
|
|
56
|
+
"--is-ancestor",
|
|
57
|
+
entry.branch,
|
|
58
|
+
`origin/${base}`
|
|
59
|
+
) !== null;
|
|
60
|
+
const remoteDeleted = gitSafe(
|
|
61
|
+
"show-ref",
|
|
62
|
+
"--verify",
|
|
63
|
+
"--quiet",
|
|
64
|
+
`refs/remotes/origin/${branch}`
|
|
65
|
+
) === null;
|
|
66
|
+
if (!merged && !remoteDeleted) continue;
|
|
67
|
+
const status = gitInSafe(entry.path, "status", "--porcelain");
|
|
68
|
+
if (status === null || status !== "") {
|
|
69
|
+
if (status !== null && status !== "") {
|
|
70
|
+
warn(`skipping ${branch} -- has uncommitted changes`);
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!remoteDeleted) {
|
|
75
|
+
const unpushed = gitInSafe(
|
|
76
|
+
entry.path,
|
|
77
|
+
"log",
|
|
78
|
+
"--oneline",
|
|
79
|
+
`origin/${base}..HEAD`
|
|
80
|
+
);
|
|
81
|
+
const count = unpushed ? unpushed.split("\n").filter(Boolean).length : 0;
|
|
82
|
+
if (count > 0) {
|
|
83
|
+
warn(`skipping ${branch} -- has ${count} unpushed commit(s)`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
candidates.push({ path: entry.path, branch });
|
|
88
|
+
}
|
|
89
|
+
if (candidates.length === 0) {
|
|
90
|
+
ok("no worktrees to clean up");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(`
|
|
94
|
+
${bold("Candidates for cleanup:")}`);
|
|
95
|
+
for (const c of candidates) {
|
|
96
|
+
console.log(` ${red("\u2715")} ${c.branch} ${dim(`(${c.path})`)}`);
|
|
97
|
+
}
|
|
98
|
+
console.log();
|
|
99
|
+
if (dryRun) {
|
|
100
|
+
info("dry run -- no worktrees deleted");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!yes) {
|
|
104
|
+
const confirmed = await confirm(
|
|
105
|
+
`Delete ${candidates.length} worktree(s)? [y/N] `
|
|
106
|
+
);
|
|
107
|
+
if (!confirmed) {
|
|
108
|
+
info("aborted");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const c of candidates) {
|
|
113
|
+
try {
|
|
114
|
+
git("worktree", "remove", c.path);
|
|
115
|
+
gitSafe("branch", "-d", c.branch);
|
|
116
|
+
unregisterWorktree(c.path);
|
|
117
|
+
ok(`deleted ${c.branch}`);
|
|
118
|
+
} catch {
|
|
119
|
+
warn(`failed to delete ${c.branch}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
function confirm(prompt) {
|
|
125
|
+
const rl = readline.createInterface({
|
|
126
|
+
input: process.stdin,
|
|
127
|
+
output: process.stdout
|
|
128
|
+
});
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
rl.question(prompt, (answer) => {
|
|
131
|
+
rl.close();
|
|
132
|
+
resolve(/^[Yy]$/.test(answer.trim()));
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
cleanup
|
|
139
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/lib/git.ts
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
function git(...args) {
|
|
4
|
+
const result = Bun.spawnSync(["git", ...args], {
|
|
5
|
+
stdout: "pipe",
|
|
6
|
+
stderr: "pipe"
|
|
7
|
+
});
|
|
8
|
+
if (!result.success) {
|
|
9
|
+
const err = new TextDecoder().decode(result.stderr);
|
|
10
|
+
throw new Error(err || `git ${args.join(" ")} failed`);
|
|
11
|
+
}
|
|
12
|
+
return new TextDecoder().decode(result.stdout).trim();
|
|
13
|
+
}
|
|
14
|
+
function gitSafe(...args) {
|
|
15
|
+
const result = Bun.spawnSync(["git", ...args], {
|
|
16
|
+
stdout: "pipe",
|
|
17
|
+
stderr: "ignore"
|
|
18
|
+
});
|
|
19
|
+
if (!result.success) return null;
|
|
20
|
+
return new TextDecoder().decode(result.stdout).trim();
|
|
21
|
+
}
|
|
22
|
+
function gitIn(cwd, ...args) {
|
|
23
|
+
const result = Bun.spawnSync(["git", ...args], {
|
|
24
|
+
cwd,
|
|
25
|
+
stdout: "pipe",
|
|
26
|
+
stderr: "pipe"
|
|
27
|
+
});
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
const err = new TextDecoder().decode(result.stderr);
|
|
30
|
+
throw new Error(err || `git ${args.join(" ")} failed in ${cwd}`);
|
|
31
|
+
}
|
|
32
|
+
return new TextDecoder().decode(result.stdout).trim();
|
|
33
|
+
}
|
|
34
|
+
function gitInSafe(cwd, ...args) {
|
|
35
|
+
const result = Bun.spawnSync(["git", ...args], {
|
|
36
|
+
cwd,
|
|
37
|
+
stdout: "pipe",
|
|
38
|
+
stderr: "ignore"
|
|
39
|
+
});
|
|
40
|
+
if (!result.success) return null;
|
|
41
|
+
return new TextDecoder().decode(result.stdout).trim();
|
|
42
|
+
}
|
|
43
|
+
function spawnShell(cwd) {
|
|
44
|
+
const shell = process.env.SHELL || "bash";
|
|
45
|
+
Bun.spawnSync([shell], {
|
|
46
|
+
cwd,
|
|
47
|
+
stdin: "inherit",
|
|
48
|
+
stdout: "inherit",
|
|
49
|
+
stderr: "inherit"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function gitRoot() {
|
|
53
|
+
const commonDir = git("rev-parse", "--git-common-dir");
|
|
54
|
+
if (!path.isAbsolute(commonDir)) {
|
|
55
|
+
return git("rev-parse", "--show-toplevel");
|
|
56
|
+
}
|
|
57
|
+
return path.dirname(commonDir);
|
|
58
|
+
}
|
|
59
|
+
function defaultBranch() {
|
|
60
|
+
return defaultBranchIn(process.cwd());
|
|
61
|
+
}
|
|
62
|
+
function defaultBranchIn(repoPath) {
|
|
63
|
+
const ref = gitInSafe(repoPath, "symbolic-ref", "refs/remotes/origin/HEAD");
|
|
64
|
+
if (ref) return ref.replace("refs/remotes/origin/", "");
|
|
65
|
+
for (const name of ["main", "master"]) {
|
|
66
|
+
if (gitInSafe(
|
|
67
|
+
repoPath,
|
|
68
|
+
"show-ref",
|
|
69
|
+
"--verify",
|
|
70
|
+
`refs/remotes/origin/${name}`
|
|
71
|
+
) !== null) {
|
|
72
|
+
return name;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const name of ["main", "master"]) {
|
|
76
|
+
if (gitInSafe(repoPath, "show-ref", "--verify", `refs/heads/${name}`) !== null) {
|
|
77
|
+
return name;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(
|
|
81
|
+
"Cannot detect default branch. Set it with: git remote set-head origin <branch>"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function currentBranchIn(wtPath) {
|
|
85
|
+
const out = gitInSafe(wtPath, "branch", "--show-current");
|
|
86
|
+
return out && out.length > 0 ? out : null;
|
|
87
|
+
}
|
|
88
|
+
function listWorktrees() {
|
|
89
|
+
const raw = git("worktree", "list", "--porcelain");
|
|
90
|
+
const entries = [];
|
|
91
|
+
let current = {};
|
|
92
|
+
for (const line of raw.split("\n")) {
|
|
93
|
+
if (line === "") {
|
|
94
|
+
if (current.path) {
|
|
95
|
+
entries.push({
|
|
96
|
+
path: current.path,
|
|
97
|
+
commit: current.commit ?? "",
|
|
98
|
+
branch: current.branch ?? null
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
current = {};
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (line.startsWith("worktree ")) current.path = line.slice(9);
|
|
105
|
+
else if (line.startsWith("HEAD ")) current.commit = line.slice(5);
|
|
106
|
+
else if (line.startsWith("branch ")) current.branch = line.slice(7);
|
|
107
|
+
}
|
|
108
|
+
if (current.path) {
|
|
109
|
+
entries.push({
|
|
110
|
+
path: current.path,
|
|
111
|
+
commit: current.commit ?? "",
|
|
112
|
+
branch: current.branch ?? null
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return entries;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
git,
|
|
120
|
+
gitSafe,
|
|
121
|
+
gitIn,
|
|
122
|
+
gitInSafe,
|
|
123
|
+
spawnShell,
|
|
124
|
+
gitRoot,
|
|
125
|
+
defaultBranch,
|
|
126
|
+
defaultBranchIn,
|
|
127
|
+
currentBranchIn,
|
|
128
|
+
listWorktrees
|
|
129
|
+
};
|