@clipboard-health/groundcrew 4.9.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -8
- package/clearance-allow-hosts +5 -1
- package/crew.config.example.ts +9 -8
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +10 -3
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +15 -6
- package/package.json +2 -2
- package/static/demo-fixture.sh +111 -0
- package/static/demo.gif +0 -0
- package/static/demo.tape +42 -0
- package/static/render-demo.sh +40 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
Dispatch your ticket backlog to AI coding agents. One git worktree per ticket, sandboxed by default.
|
|
9
|
+
Dispatch your ticket backlog to local, interactive AI coding agents. One git worktree per ticket, sandboxed by default.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -17,16 +17,22 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<img alt="Groundcrew
|
|
20
|
+
<a href="./static/demo.tape"><img alt="Groundcrew dispatching tickets into tmux panes with coding agents running in parallel" src="./static/demo.gif" width="800"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
VHS source: <a href="./static/demo.tape">static/demo.tape</a>.
|
|
21
25
|
</p>
|
|
22
26
|
|
|
23
27
|
Groundcrew watches assigned tickets, creates isolated worktrees, launches agent CLIs in dedicated terminals, and leaves each ticket's work on its own PR-ready branch. For the backstory, read _[Tickets to pull requests while you sleep](https://www.clipboardworks.com/resources/blog/tickets-to-pull-requests-while-you-sleep)_.
|
|
24
28
|
|
|
25
29
|
## Why
|
|
26
30
|
|
|
31
|
+
- **Local.** Agents run on your machine with your tools, shell, and credentials. That makes them more steerable than remote agents, and easy to nudge when they drift.
|
|
32
|
+
- **Interactive.** Each ticket launches the real `claude` or `codex` CLI in its own terminal pane, not a wrapper that approximates it. Watch any session live and take over when you need to.
|
|
27
33
|
- **One worktree per ticket.** Agents work in parallel without stepping on each other.
|
|
34
|
+
- **Sandboxed by default.** Safehouse or Docker Sandboxes isolate each agent on the host; `none` is an explicit escape hatch.
|
|
28
35
|
- **Pluggable ticket sources.** Linear by default; Jira and local files via [ticket sources](./docs/ticket-sources.md).
|
|
29
|
-
- **Local-first isolation.** Safehouse, Docker Sandboxes, or an explicit `none` escape hatch.
|
|
30
36
|
- **Multi-agent routing.** Ships `claude` and `codex` presets; bring your own CLI in config.
|
|
31
37
|
|
|
32
38
|
## Prerequisites
|
|
@@ -83,8 +89,7 @@ crew init [--global | --local] [--force] [--dry-run] # create a crew.config.
|
|
|
83
89
|
[--runner <auto|safehouse|sdx|none>] [--model <claude|codex>]
|
|
84
90
|
crew doctor # check setup
|
|
85
91
|
crew status [<TICKET>] # inspect current state or one ticket
|
|
86
|
-
crew run
|
|
87
|
-
crew run --watch # poll forever
|
|
92
|
+
crew run [--watch] # one-shot or --watch forever
|
|
88
93
|
crew start <TICKET> # provision + launch one ticket now
|
|
89
94
|
crew stop <TICKET> [--reason <text>] # stop workspace, keep worktree
|
|
90
95
|
crew resume <TICKET> # reopen a paused ticket
|
|
@@ -106,15 +111,18 @@ export default {
|
|
|
106
111
|
projectDir: "~/dev",
|
|
107
112
|
knownRepositories: ["OWNER/REPO"],
|
|
108
113
|
},
|
|
109
|
-
local: {
|
|
110
|
-
runner: "auto",
|
|
111
|
-
},
|
|
112
114
|
models: {
|
|
113
115
|
default: "claude",
|
|
114
116
|
definitions: {
|
|
115
117
|
claude: {},
|
|
116
118
|
},
|
|
117
119
|
},
|
|
120
|
+
defaults: {
|
|
121
|
+
hooks: {
|
|
122
|
+
// No-op placeholder; replace with your repo's setup, e.g. "npm ci".
|
|
123
|
+
prepareWorktree: "true",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
118
126
|
} satisfies Config;
|
|
119
127
|
```
|
|
120
128
|
|
|
@@ -143,6 +151,12 @@ node --run crew:op -- run --watch
|
|
|
143
151
|
|
|
144
152
|
Both forms discover config through cosmiconfig. Source edits in `src/**` are picked up on the next invocation. Requires Node >= 24.
|
|
145
153
|
|
|
154
|
+
Regenerate the README demo with VHS:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
./static/render-demo.sh
|
|
158
|
+
```
|
|
159
|
+
|
|
146
160
|
## License
|
|
147
161
|
|
|
148
162
|
[MIT](./LICENSE)
|
package/clearance-allow-hosts
CHANGED
|
@@ -64,8 +64,9 @@ raw.githubusercontent.com
|
|
|
64
64
|
release-assets.githubusercontent.com
|
|
65
65
|
results-receiver.actions.githubusercontent.com
|
|
66
66
|
|
|
67
|
-
# npm
|
|
67
|
+
# npm/Conda registries + package websites
|
|
68
68
|
api.npmjs.org
|
|
69
|
+
conda.anaconda.org
|
|
69
70
|
registry.npmjs.org
|
|
70
71
|
www.npmjs.com
|
|
71
72
|
|
|
@@ -83,6 +84,9 @@ formulae.brew.sh
|
|
|
83
84
|
hub.docker.com
|
|
84
85
|
index.docker.io
|
|
85
86
|
json.schemastore.org
|
|
87
|
+
mise-versions.jdx.dev
|
|
86
88
|
nx.dev
|
|
89
|
+
playwright.azureedge.net
|
|
90
|
+
registry.terraform.io
|
|
87
91
|
sourcegraph.com
|
|
88
92
|
vitest.dev
|
package/crew.config.example.ts
CHANGED
|
@@ -35,6 +35,15 @@ export default {
|
|
|
35
35
|
// },
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
|
+
// Repo-preparation hook: runs after each worktree is created and before the
|
|
39
|
+
// agent launches. The default below is a no-op placeholder. Replace it with
|
|
40
|
+
// your repo's setup, e.g. "npm ci" or "uv sync --dev --frozen". A repo-local
|
|
41
|
+
// `.groundcrew/config.json` hooks.prepareWorktree overrides this per repo.
|
|
42
|
+
defaults: {
|
|
43
|
+
hooks: {
|
|
44
|
+
prepareWorktree: "true",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
38
47
|
// Everything below is optional — defaults shown for reference. Uncomment
|
|
39
48
|
// and edit to override.
|
|
40
49
|
//
|
|
@@ -59,14 +68,6 @@ export default {
|
|
|
59
68
|
//
|
|
60
69
|
// git: { remote: "origin", defaultBranch: "main" },
|
|
61
70
|
//
|
|
62
|
-
// // Fallback repo-preparation hook for repos that do not define
|
|
63
|
-
// // `.groundcrew/config.json` hooks.prepareWorktree. Repo-local config wins.
|
|
64
|
-
// defaults: {
|
|
65
|
-
// hooks: {
|
|
66
|
-
// prepareWorktree: "test ! -f package-lock.json || npm ci",
|
|
67
|
-
// },
|
|
68
|
-
// },
|
|
69
|
-
//
|
|
70
71
|
// orchestrator: {
|
|
71
72
|
// maximumInProgress: 4,
|
|
72
73
|
// pollIntervalMilliseconds: 120_000,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA8KH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAgF/C"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { existsSync, statSync } from "node:fs";
|
|
6
6
|
import { createBoard } from "../lib/board.js";
|
|
7
7
|
import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
|
|
8
|
-
import {
|
|
8
|
+
import { loadConfigWithSource, } from "../lib/config.js";
|
|
9
9
|
import { detectHostCapabilities, which } from "../lib/host.js";
|
|
10
10
|
import { resolveLocalRunner } from "../lib/localRunner.js";
|
|
11
11
|
import { gatedModels } from "../lib/usage.js";
|
|
@@ -15,6 +15,11 @@ import { resolveWorkspaceKind } from "../lib/workspaces.js";
|
|
|
15
15
|
// catch wrapper + wrapped CLI commands like `safehouse claude --foo`.
|
|
16
16
|
const MAX_TOKENS_PER_CMD = 2;
|
|
17
17
|
const BUILT_IN_MODEL_NAMES = ["claude", "codex"];
|
|
18
|
+
const CONFIG_SOURCE_LABELS = {
|
|
19
|
+
env: "GROUNDCREW_CONFIG",
|
|
20
|
+
project: "project",
|
|
21
|
+
xdg: "global XDG",
|
|
22
|
+
};
|
|
18
23
|
async function checkCmd(cmd, required, hint) {
|
|
19
24
|
const path = await which(cmd);
|
|
20
25
|
const resolvedHint = path ?? hint;
|
|
@@ -148,8 +153,10 @@ export async function doctor() {
|
|
|
148
153
|
writeOutput("=================");
|
|
149
154
|
let config;
|
|
150
155
|
try {
|
|
151
|
-
config = await
|
|
152
|
-
|
|
156
|
+
const { config: loadedConfig, source } = await loadConfigWithSource();
|
|
157
|
+
config = loadedConfig;
|
|
158
|
+
const sourceLabel = CONFIG_SOURCE_LABELS[source.kind];
|
|
159
|
+
writeOutput(`[ok] config loaded — ${source.filepath} (${sourceLabel})`);
|
|
153
160
|
}
|
|
154
161
|
catch (error) {
|
|
155
162
|
writeOutput(`[--] config: ${errorMessage(error)}`);
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -241,6 +241,15 @@ export interface ResolvedConfig {
|
|
|
241
241
|
file: string;
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
|
+
export type ConfigSourceKind = "env" | "project" | "xdg";
|
|
245
|
+
export interface ConfigSource {
|
|
246
|
+
kind: ConfigSourceKind;
|
|
247
|
+
filepath: string;
|
|
248
|
+
}
|
|
249
|
+
export interface LoadedConfig {
|
|
250
|
+
config: Readonly<ResolvedConfig>;
|
|
251
|
+
source: Readonly<ConfigSource>;
|
|
252
|
+
}
|
|
244
253
|
/**
|
|
245
254
|
* Single source of truth for "is preLaunchEnv asking us to forward anything?"
|
|
246
255
|
*
|
|
@@ -258,5 +267,6 @@ export declare function hasPreLaunchEnv(definition: Pick<ModelDefinition, "preLa
|
|
|
258
267
|
* not enabled from an arbitrary unknown label like `agent-typo`.
|
|
259
268
|
*/
|
|
260
269
|
export declare function isBuiltInModelNotEnabled(config: Pick<ResolvedConfig, "models">, name: string): boolean;
|
|
270
|
+
export declare function loadConfigWithSource(): Promise<Readonly<LoadedConfig>>;
|
|
261
271
|
export declare function loadConfig(): Promise<Readonly<ResolvedConfig>>;
|
|
262
272
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -606,26 +606,28 @@ async function discoverUserConfig() {
|
|
|
606
606
|
if (!existsSync(overridePath)) {
|
|
607
607
|
fail(`GROUNDCREW_CONFIG=${overridePath} not found`);
|
|
608
608
|
}
|
|
609
|
-
|
|
609
|
+
const result = await loadAt(overridePath);
|
|
610
|
+
return { result, source: { kind: "env", filepath: result.filepath } };
|
|
610
611
|
}
|
|
611
612
|
const project = await explorer.search(process.cwd());
|
|
612
613
|
if (project !== null && project.isEmpty !== true) {
|
|
613
|
-
return project;
|
|
614
|
+
return { result: project, source: { kind: "project", filepath: project.filepath } };
|
|
614
615
|
}
|
|
615
616
|
const xdgPath = findXdgConfigFile();
|
|
616
617
|
if (xdgPath !== undefined) {
|
|
617
|
-
|
|
618
|
+
const result = await loadAt(xdgPath);
|
|
619
|
+
return { result, source: { kind: "xdg", filepath: result.filepath } };
|
|
618
620
|
}
|
|
619
621
|
// Throw directly so oxlint's `consistent-return` rule sees a
|
|
620
622
|
// terminating statement; it doesn't track `fail()`'s `never` return.
|
|
621
623
|
throw new Error(`groundcrew config: no crew config found. Create crew.config.ts in your project root, or ${xdgConfigPath("groundcrew", "crew.config.ts")}, or set GROUNDCREW_CONFIG.`);
|
|
622
624
|
}
|
|
623
625
|
let cached;
|
|
624
|
-
export async function
|
|
626
|
+
export async function loadConfigWithSource() {
|
|
625
627
|
if (cached) {
|
|
626
628
|
return cached;
|
|
627
629
|
}
|
|
628
|
-
const result = await discoverUserConfig();
|
|
630
|
+
const { result, source } = await discoverUserConfig();
|
|
629
631
|
const { filepath, isEmpty } = result;
|
|
630
632
|
const userConfig = result.config;
|
|
631
633
|
if (isEmpty === true || !isPlainObject(userConfig)) {
|
|
@@ -636,6 +638,13 @@ export async function loadConfig() {
|
|
|
636
638
|
const resolved = applyDefaults(userConfig);
|
|
637
639
|
validate(resolved);
|
|
638
640
|
setLogFile(resolved.logging.file);
|
|
639
|
-
cached = Object.freeze(
|
|
641
|
+
cached = Object.freeze({
|
|
642
|
+
config: Object.freeze(resolved),
|
|
643
|
+
source: Object.freeze(source),
|
|
644
|
+
});
|
|
640
645
|
return cached;
|
|
641
646
|
}
|
|
647
|
+
export async function loadConfig() {
|
|
648
|
+
const loadedConfig = await loadConfigWithSource();
|
|
649
|
+
return loadedConfig.config;
|
|
650
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
85
85
|
"@vitest/coverage-v8": "4.1.7",
|
|
86
86
|
"cspell": "10.0.0",
|
|
87
|
-
"dependency-cruiser": "17.4.
|
|
87
|
+
"dependency-cruiser": "17.4.3",
|
|
88
88
|
"husky": "9.1.7",
|
|
89
89
|
"jscpd": "4.2.4",
|
|
90
90
|
"knip": "6.14.2",
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Semantic ANSI colors. Actual hues are defined by the VHS theme in demo.tape,
|
|
5
|
+
# so content and chrome stay in sync: green = orchestrator, yellow = codex,
|
|
6
|
+
# blue = claude, bright-black = dim chrome.
|
|
7
|
+
c_reset=$'\033[0m'
|
|
8
|
+
c_dim=$'\033[90m'
|
|
9
|
+
c_green=$'\033[32m'
|
|
10
|
+
c_amber=$'\033[33m'
|
|
11
|
+
c_blue=$'\033[34m'
|
|
12
|
+
|
|
13
|
+
export PS1="\[${c_green}\]groundcrew\[${c_dim}\] on \[${c_reset}\]main \[${c_green}\]\$\[${c_reset}\] "
|
|
14
|
+
|
|
15
|
+
tmux rename-window 'crew run --watch'
|
|
16
|
+
tmux set-option -g status off
|
|
17
|
+
tmux set-option -g pane-border-status top
|
|
18
|
+
tmux set-option -g pane-border-format ' #{?#{m:codex*,#{pane_title}},#[fg=#fbbf24],#{?#{m:claude*,#{pane_title}},#[fg=#60a5fa],#[fg=#77d94e]}}#[bold]#{pane_title}#[default] '
|
|
19
|
+
tmux set-option -g pane-border-style 'fg=#3f3f46'
|
|
20
|
+
tmux set-option -g pane-active-border-style 'fg=#77d94e'
|
|
21
|
+
tmux set-option -g remain-on-exit on
|
|
22
|
+
|
|
23
|
+
printf '\033]2;groundcrew\033\\'
|
|
24
|
+
|
|
25
|
+
demo_agent_script="$(mktemp "${TMPDIR:-/tmp}/groundcrew-vhs-agent.XXXXXX")"
|
|
26
|
+
trap 'rm -f "${demo_agent_script}"' EXIT
|
|
27
|
+
cat >"${demo_agent_script}" <<'SH'
|
|
28
|
+
#!/usr/bin/env bash
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
ticket="${1}"
|
|
32
|
+
model="${2}"
|
|
33
|
+
title="${3}"
|
|
34
|
+
worktree="${4:-groundcrew-${ticket}}"
|
|
35
|
+
branch="groundcrew/${ticket}"
|
|
36
|
+
|
|
37
|
+
reset=$'\033[0m'
|
|
38
|
+
bold=$'\033[1m'
|
|
39
|
+
dim=$'\033[90m'
|
|
40
|
+
green=$'\033[32m'
|
|
41
|
+
case "${model}" in
|
|
42
|
+
codex) accent=$'\033[33m' ;;
|
|
43
|
+
claude) accent=$'\033[34m' ;;
|
|
44
|
+
*) accent=$'\033[32m' ;;
|
|
45
|
+
esac
|
|
46
|
+
|
|
47
|
+
printf '\033]2;%s %s\033\\' "${model}" "${ticket}"
|
|
48
|
+
printf '%s%s%s clipboard/groundcrew\n' "${dim}" 'repo' "${reset}"
|
|
49
|
+
printf '%s%s%s %s\n\n' "${dim}" 'branch' "${reset}" "${branch}"
|
|
50
|
+
sleep 0.4
|
|
51
|
+
printf '%s$%s %s%s%s %s< prompts/%s.txt%s\n\n' "${dim}" "${reset}" "${accent}" "${model}" "${reset}" "${dim}" "${ticket}" "${reset}"
|
|
52
|
+
sleep 0.5
|
|
53
|
+
printf '%sTicket%s %s\n' "${dim}" "${reset}" "${title}"
|
|
54
|
+
sleep 0.4
|
|
55
|
+
printf '%sWorktree%s %s~/dev/c/%s%s\n\n' "${dim}" "${reset}" "${dim}" "${worktree}" "${reset}"
|
|
56
|
+
sleep 0.5
|
|
57
|
+
printf '%sReading repo context...%s\n' "${dim}" "${reset}"
|
|
58
|
+
sleep 0.5
|
|
59
|
+
printf '%sEditing in isolated branch...%s\n' "${dim}" "${reset}"
|
|
60
|
+
sleep 0.5
|
|
61
|
+
printf '%sRunning verification...%s\n' "${dim}" "${reset}"
|
|
62
|
+
sleep 0.5
|
|
63
|
+
printf '%s%s✓ Ready for review.%s\n' "${green}" "${bold}" "${reset}"
|
|
64
|
+
|
|
65
|
+
while :; do
|
|
66
|
+
sleep 60
|
|
67
|
+
done
|
|
68
|
+
SH
|
|
69
|
+
chmod +x "${demo_agent_script}"
|
|
70
|
+
|
|
71
|
+
demo_ts_second=18
|
|
72
|
+
|
|
73
|
+
demo_log() {
|
|
74
|
+
printf '%s[15:23:%02d]%s %s\n' "${c_dim}" "${demo_ts_second}" "${c_reset}" "${1}"
|
|
75
|
+
demo_ts_second=$((demo_ts_second + 1))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
crew() {
|
|
79
|
+
if [[ "${1:-}" != "run" || "${2:-}" != "--watch" ]]; then
|
|
80
|
+
printf '%s\n' 'demo supports: crew run --watch'
|
|
81
|
+
return 2
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
local pane_one
|
|
85
|
+
|
|
86
|
+
demo_log "${c_dim}Linear viewer ·${c_reset} Rocky Warren"
|
|
87
|
+
sleep 0.5
|
|
88
|
+
demo_log "${c_dim}Slots${c_reset} 0/3 ${c_dim}· dispatching${c_reset} ${c_amber}ENG-184${c_reset}${c_dim},${c_reset} ${c_blue}ENG-217${c_reset}"
|
|
89
|
+
sleep 0.6
|
|
90
|
+
|
|
91
|
+
demo_log "${c_dim}Worktree${c_reset} web-ENG-184 ${c_dim}→${c_reset} groundcrew/ENG-184"
|
|
92
|
+
sleep 0.5
|
|
93
|
+
pane_one="$(
|
|
94
|
+
tmux split-window -d -h -p 42 -P -F '#{pane_id}' -- \
|
|
95
|
+
"${demo_agent_script} ENG-184 codex 'Add Jira ticket source docs' web-ENG-184"
|
|
96
|
+
)"
|
|
97
|
+
sleep 0.7
|
|
98
|
+
demo_log "${c_green}✓${c_reset} ${c_amber}ENG-184${c_reset} launched ${c_dim}·${c_reset} ${c_amber}codex${c_reset}"
|
|
99
|
+
sleep 0.7
|
|
100
|
+
|
|
101
|
+
demo_log "${c_dim}Worktree${c_reset} api-ENG-217 ${c_dim}→${c_reset} groundcrew/ENG-217"
|
|
102
|
+
sleep 0.5
|
|
103
|
+
tmux split-window -d -v -p 50 -t "${pane_one}" -P -F '#{pane_id}' -- \
|
|
104
|
+
"${demo_agent_script} ENG-217 claude 'Fix flaky status output' api-ENG-217" >/dev/null
|
|
105
|
+
sleep 0.7
|
|
106
|
+
demo_log "${c_green}✓${c_reset} ${c_blue}ENG-217${c_reset} launched ${c_dim}·${c_reset} ${c_blue}claude${c_reset}"
|
|
107
|
+
sleep 0.7
|
|
108
|
+
|
|
109
|
+
demo_log "${c_dim}Queue clear · next poll in 60s${c_reset}"
|
|
110
|
+
sleep 8
|
|
111
|
+
}
|
package/static/demo.gif
CHANGED
|
Binary file
|
package/static/demo.tape
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# cspell:ignore noprofile norc ttyd
|
|
2
|
+
|
|
3
|
+
Output static/demo.gif
|
|
4
|
+
Require bash
|
|
5
|
+
Require ffmpeg
|
|
6
|
+
Require tmux
|
|
7
|
+
Require ttyd
|
|
8
|
+
|
|
9
|
+
Set Shell "bash"
|
|
10
|
+
Set Width 1320
|
|
11
|
+
Set Height 560
|
|
12
|
+
Set FontSize 16
|
|
13
|
+
Set FontFamily "Menlo"
|
|
14
|
+
Set Padding 16
|
|
15
|
+
Set Margin 40
|
|
16
|
+
Set MarginFill "#0a0a0b"
|
|
17
|
+
Set WindowBar Colorful
|
|
18
|
+
Set BorderRadius 12
|
|
19
|
+
Set TypingSpeed 35ms
|
|
20
|
+
Set CursorBlink false
|
|
21
|
+
Set Theme '{ "background": "#18181b", "foreground": "#e4e4e7", "cursor": "#77d94e", "selection": "#3f3f46", "black": "#27272a", "red": "#fb7185", "green": "#77d94e", "yellow": "#fbbf24", "blue": "#60a5fa", "magenta": "#c084fc", "cyan": "#34d399", "white": "#e4e4e7", "brightBlack": "#52525b", "brightRed": "#fda4af", "brightGreen": "#a3e635", "brightYellow": "#fcd34d", "brightBlue": "#93c5fd", "brightMagenta": "#d8b4fe", "brightCyan": "#6ee7b7", "brightWhite": "#fafafa" }'
|
|
22
|
+
|
|
23
|
+
Hide
|
|
24
|
+
Type "tmux -f /dev/null -L groundcrew-demo kill-session -t groundcrew 2>/dev/null || true"
|
|
25
|
+
Enter
|
|
26
|
+
Type "tmux -f /dev/null -L groundcrew-demo new-session -s groundcrew -- /bin/bash --noprofile --norc"
|
|
27
|
+
Enter
|
|
28
|
+
Type "source static/demo-fixture.sh"
|
|
29
|
+
Enter
|
|
30
|
+
Type "clear"
|
|
31
|
+
Enter
|
|
32
|
+
Show
|
|
33
|
+
|
|
34
|
+
Sleep 1.0
|
|
35
|
+
Type "crew run --watch"
|
|
36
|
+
Sleep 0.4
|
|
37
|
+
Enter
|
|
38
|
+
Sleep 9
|
|
39
|
+
|
|
40
|
+
Hide
|
|
41
|
+
Type "tmux -f /dev/null -L groundcrew-demo kill-session -t groundcrew"
|
|
42
|
+
Enter
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cspell:ignore ttyd
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
repo_dir="$(cd -- "${script_dir}/.." && pwd)"
|
|
7
|
+
cd "${repo_dir}"
|
|
8
|
+
|
|
9
|
+
for required_command in vhs ttyd tmux ffmpeg; do
|
|
10
|
+
if ! command -v "${required_command}" >/dev/null 2>&1; then
|
|
11
|
+
printf 'Missing required command: %s\n' "${required_command}" >&2
|
|
12
|
+
exit 127
|
|
13
|
+
fi
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
tmux -f /dev/null -L groundcrew-demo kill-session -t groundcrew 2>/dev/null || true
|
|
17
|
+
|
|
18
|
+
log_file="$(mktemp)"
|
|
19
|
+
trap 'rm -f "${log_file}"' EXIT
|
|
20
|
+
|
|
21
|
+
for attempt in 1 2 3; do
|
|
22
|
+
rm -f "${log_file}"
|
|
23
|
+
|
|
24
|
+
if VHS_NO_SANDBOX="${VHS_NO_SANDBOX:-1}" vhs static/demo.tape 2>&1 | tee "${log_file}"; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
status="${PIPESTATUS[0]}"
|
|
29
|
+
tmux -f /dev/null -L groundcrew-demo kill-session -t groundcrew 2>/dev/null || true
|
|
30
|
+
|
|
31
|
+
if ! grep -Eq 'could not open ttyd|ERR_CONNECTION_REFUSED' "${log_file}"; then
|
|
32
|
+
exit "${status}"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [[ "${attempt}" -eq 3 ]]; then
|
|
36
|
+
exit "${status}"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
sleep "${attempt}"
|
|
40
|
+
done
|