@clipboard-health/groundcrew 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +15 -8
- package/dist/lib/boardSource.js +1 -1
- package/dist/lib/util.d.ts +7 -0
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +14 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,18 +49,20 @@ This installs the `crew` binary. `@clipboard-health/clearance` is pulled in tran
|
|
|
49
49
|
|
|
50
50
|
`crew` resolves the config path as: `GROUNDCREW_CONFIG` if set → `${XDG_CONFIG_HOME:-$HOME/.config}/groundcrew/config.ts` if it exists → a `config.ts` sitting next to `crew`'s own source files (only useful from a local checkout; see [Hacking on groundcrew](#hacking-on-groundcrew)). Set `GROUNDCREW_CONFIG` only when you want to override the XDG location.
|
|
51
51
|
|
|
52
|
-
4. **Provide a Linear API key.** `crew`
|
|
52
|
+
4. **Provide a Linear API key.** `crew` reads the key from `GROUNDCREW_LINEAR_API_KEY` first, then falls back to `LINEAR_API_KEY`. Prefer `GROUNDCREW_LINEAR_API_KEY` so the value does not clash with other tools that consume `LINEAR_API_KEY`. Any mechanism works — shell export, [direnv](https://direnv.net/), a `.env` file you `source`, or piping through `op run` if you store the credential in 1Password:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
# Direct
|
|
56
|
-
export
|
|
56
|
+
export GROUNDCREW_LINEAR_API_KEY="lin_api_..."
|
|
57
57
|
crew doctor
|
|
58
58
|
|
|
59
59
|
# Via 1Password CLI (`op`), if you keep the key in a vault
|
|
60
|
-
echo "
|
|
60
|
+
echo "GROUNDCREW_LINEAR_API_KEY='op://<vault>/LINEAR_API_KEY/credential'" > .env.1password
|
|
61
61
|
op run --env-file .env.1password -- crew doctor
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
`LINEAR_API_KEY` continues to work for existing setups; if both variables are set, `GROUNDCREW_LINEAR_API_KEY` wins.
|
|
65
|
+
|
|
64
66
|
5. **Prepare the runner and agent auth.** Groundcrew supports one runner: a `cmux` or `tmux` workspace on macOS, with Safehouse on `PATH`, `clearance`, and locally authenticated agent CLIs.
|
|
65
67
|
|
|
66
68
|
Setup fails before creating a worktree when the host is not macOS or `safehouse` is missing. `models.isolation`, per-model `isolation`, and per-model `sandbox` are legacy keys and now fail config validation.
|
|
@@ -185,7 +187,7 @@ For developers working on the package itself, clone this repo, run `npm install`
|
|
|
185
187
|
cd ~/dev/c/groundcrew
|
|
186
188
|
node --run crew -- doctor
|
|
187
189
|
|
|
188
|
-
# With 1Password for
|
|
190
|
+
# With 1Password for GROUNDCREW_LINEAR_API_KEY:
|
|
189
191
|
node --run crew:op -- run --watch
|
|
190
192
|
```
|
|
191
193
|
|
|
@@ -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;AAwIH,wBAAsB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CA6D/C"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { existsSync, statSync } from "node:fs";
|
|
6
6
|
import { loadConfig } from "../lib/config.js";
|
|
7
7
|
import { detectHostCapabilities, which } from "../lib/host.js";
|
|
8
|
-
import { errorMessage,
|
|
8
|
+
import { errorMessage, resolveLinearApiKey, writeOutput } from "../lib/util.js";
|
|
9
9
|
import { resolveWorkspaceKind } from "../lib/workspaces.js";
|
|
10
10
|
// Tokenization stops after this many non-flag tokens. Two is enough to
|
|
11
11
|
// catch wrapper + wrapped CLI commands like `safehouse claude --foo`.
|
|
@@ -23,14 +23,21 @@ async function checkCmd(cmd, required, hint) {
|
|
|
23
23
|
}
|
|
24
24
|
return result;
|
|
25
25
|
}
|
|
26
|
-
function
|
|
27
|
-
const
|
|
28
|
-
|
|
26
|
+
function checkLinearApiKey() {
|
|
27
|
+
const resolved = resolveLinearApiKey();
|
|
28
|
+
if (resolved !== undefined) {
|
|
29
|
+
return {
|
|
30
|
+
name: "linear api key",
|
|
31
|
+
ok: true,
|
|
32
|
+
required: true,
|
|
33
|
+
hint: `set via $${resolved.source}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
29
36
|
return {
|
|
30
|
-
name:
|
|
31
|
-
ok:
|
|
37
|
+
name: "linear api key",
|
|
38
|
+
ok: false,
|
|
32
39
|
required: true,
|
|
33
|
-
hint:
|
|
40
|
+
hint: "export $GROUNDCREW_LINEAR_API_KEY or $LINEAR_API_KEY",
|
|
34
41
|
};
|
|
35
42
|
}
|
|
36
43
|
function checkDir(path, label) {
|
|
@@ -138,7 +145,7 @@ export async function doctor() {
|
|
|
138
145
|
const workspaceOutcome = resolveWorkspaceOutcome(config, host);
|
|
139
146
|
reportWorkspaceKind(config, workspaceOutcome);
|
|
140
147
|
const checks = [
|
|
141
|
-
|
|
148
|
+
checkLinearApiKey(),
|
|
142
149
|
await checkCmd("git", true, "https://git-scm.com/"),
|
|
143
150
|
...(await workspaceChecks(workspaceOutcome)),
|
|
144
151
|
checkDir(config.workspace.projectDir, "workspace.projectDir"),
|
package/dist/lib/boardSource.js
CHANGED
|
@@ -41,7 +41,7 @@ async function verifyProject(client, config) {
|
|
|
41
41
|
const { projects } = response.data;
|
|
42
42
|
const [project] = projects.nodes;
|
|
43
43
|
if (!project) {
|
|
44
|
-
throw new Error(`No Linear project found with slugId "${config.linear.slugId}" (linear.projectSlug = "${config.linear.projectSlug}"). Confirm the slug matches the trailing segment of your project's URL and that
|
|
44
|
+
throw new Error(`No Linear project found with slugId "${config.linear.slugId}" (linear.projectSlug = "${config.linear.projectSlug}"). Confirm the slug matches the trailing segment of your project's URL and that your Linear API key can access this workspace.`);
|
|
45
45
|
}
|
|
46
46
|
log(`Resolved Linear project: ${project.name} (slugId ${project.slugId})`);
|
|
47
47
|
}
|
package/dist/lib/util.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export declare function log(message: string): void;
|
|
|
8
8
|
type LogEventFieldValue = boolean | number | string | readonly string[] | undefined;
|
|
9
9
|
export declare function logEvent(event: string, fields: Record<string, LogEventFieldValue>): void;
|
|
10
10
|
export declare function readEnvironmentVariable(name: string): string | undefined;
|
|
11
|
+
declare const LINEAR_API_KEY_SOURCES: readonly ["GROUNDCREW_LINEAR_API_KEY", "LINEAR_API_KEY"];
|
|
12
|
+
export type LinearApiKeySource = (typeof LINEAR_API_KEY_SOURCES)[number];
|
|
13
|
+
export interface ResolvedLinearApiKey {
|
|
14
|
+
value: string;
|
|
15
|
+
source: LinearApiKeySource;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveLinearApiKey(): ResolvedLinearApiKey | undefined;
|
|
11
18
|
export declare function getLinearClient(): LinearClient;
|
|
12
19
|
export declare function errorMessage(error: unknown): string;
|
|
13
20
|
export {};
|
package/dist/lib/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAOD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEzD;AAkBD,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKzC;AAED,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAUpF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAWxF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,wBAAgB,eAAe,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/lib/util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAOD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEzD;AAkBD,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKzC;AAED,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAUpF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAWxF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,QAAA,MAAM,sBAAsB,YAAI,2BAA2B,EAAE,gBAAgB,CAAU,CAAC;AAExF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,IAAI,oBAAoB,GAAG,SAAS,CAQtE;AAED,wBAAgB,eAAe,IAAI,YAAY,CAQ9C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAcnD"}
|
package/dist/lib/util.js
CHANGED
|
@@ -87,12 +87,22 @@ export function readEnvironmentVariable(name) {
|
|
|
87
87
|
// oxlint-disable-next-line node/no-process-env -- Centralized environment accessor.
|
|
88
88
|
return process.env[name];
|
|
89
89
|
}
|
|
90
|
+
const LINEAR_API_KEY_SOURCES = ["GROUNDCREW_LINEAR_API_KEY", "LINEAR_API_KEY"];
|
|
91
|
+
export function resolveLinearApiKey() {
|
|
92
|
+
for (const source of LINEAR_API_KEY_SOURCES) {
|
|
93
|
+
const value = readEnvironmentVariable(source);
|
|
94
|
+
if (value !== undefined && value.length > 0) {
|
|
95
|
+
return { value, source };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
90
100
|
export function getLinearClient() {
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
throw new Error("
|
|
101
|
+
const resolved = resolveLinearApiKey();
|
|
102
|
+
if (resolved === undefined) {
|
|
103
|
+
throw new Error("Linear API key not set. Set GROUNDCREW_LINEAR_API_KEY or LINEAR_API_KEY in your environment.");
|
|
94
104
|
}
|
|
95
|
-
return new LinearClient({ apiKey });
|
|
105
|
+
return new LinearClient({ apiKey: resolved.value });
|
|
96
106
|
}
|
|
97
107
|
export function errorMessage(error) {
|
|
98
108
|
if (error instanceof Error) {
|
package/package.json
CHANGED