@clipboard-health/groundcrew 4.3.0 → 4.3.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.
@@ -7,6 +7,7 @@ export interface UpgradeCliOptions {
7
7
  version: string;
8
8
  npmBin: string;
9
9
  }) => Promise<NpmRunResult>;
10
+ readInstalledVersion: (installPath: string) => string | undefined;
10
11
  }
11
12
  export interface UpgradeInstallDetails {
12
13
  installKind: InstallKind;
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAK7B,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACrD,UAAU,EAAE,CAAC,OAAO,EAAE;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAOD,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,CAAC,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAiD5F,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,YAAY,EAAE,sBAAsB,GACnC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAsCD,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CAqB5B"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAKA,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAK7B,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACrD,UAAU,EAAE,CAAC,OAAO,EAAE;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5B,oBAAoB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACnE;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAYD,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,CAAC,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAiD5F,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EAAE,EACd,YAAY,EAAE,sBAAsB,GACnC,OAAO,CAAC,IAAI,CAAC,CAkBf;AAmED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CAsB5B"}
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
1
3
  import { runCommand } from "../lib/commandRunner.js";
2
4
  import { which } from "../lib/host.js";
3
5
  import { classifyInstall, createDefaultNpmSpawner, detectInstallPath, detectIsSymlink, detectNpmRootGlobal, runNpmInstallGlobal, } from "../lib/npmGlobal.js";
@@ -54,13 +56,13 @@ export async function upgradeCli(argv, optionsInput) {
54
56
  return;
55
57
  }
56
58
  const options = await resolveOptions(optionsInput);
57
- const npmBin = await resolveGlobalNpmBin(options);
58
- if (npmBin === undefined) {
59
+ const install = await resolveGlobalInstall(options);
60
+ if (install === undefined) {
59
61
  return;
60
62
  }
61
- await runInstallAndReport(options, npmBin, parsed.version);
63
+ await runInstallAndReport(options, install, parsed.version);
62
64
  }
63
- async function resolveGlobalNpmBin(options) {
65
+ async function resolveGlobalInstall(options) {
64
66
  const install = await options.resolveInstall();
65
67
  if (install.installKind !== "global") {
66
68
  writeError(refusalMessage(install.installKind, install.installPath, options.packageName));
@@ -72,22 +74,45 @@ async function resolveGlobalNpmBin(options) {
72
74
  process.exitCode = 1;
73
75
  return undefined;
74
76
  }
75
- return install.npmBin;
77
+ return { installPath: install.installPath, npmBin: install.npmBin };
76
78
  }
77
- async function runInstallAndReport(options, npmBin, version) {
79
+ async function runInstallAndReport(options, install, version) {
80
+ const fromVersion = options.readInstalledVersion(install.installPath);
81
+ writeOutput("Upgrading crew…");
78
82
  const result = await options.runInstall({
79
83
  packageName: options.packageName,
80
84
  version,
81
- npmBin,
85
+ npmBin: install.npmBin,
82
86
  });
83
87
  if (result.exitCode === 0) {
88
+ const toVersion = options.readInstalledVersion(install.installPath);
89
+ writeOutput(formatUpgradeSuccess({ fromVersion, toVersion }));
84
90
  return;
85
91
  }
92
+ if (result.outputText.length > 0) {
93
+ process.stderr.write(result.outputText);
94
+ if (!result.outputText.endsWith("\n")) {
95
+ process.stderr.write("\n");
96
+ }
97
+ }
86
98
  if (result.sawEacces) {
87
99
  writeError("crew upgrade: install failed with EACCES (permission denied). Your global npm prefix may require elevated permissions - see https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally");
88
100
  }
89
101
  process.exitCode = result.exitCode;
90
102
  }
103
+ function formatUpgradeSuccess(versions) {
104
+ const { fromVersion, toVersion } = versions;
105
+ if (toVersion === undefined) {
106
+ return "crew upgrade complete";
107
+ }
108
+ if (fromVersion === undefined) {
109
+ return `crew is now on version ${toVersion}`;
110
+ }
111
+ if (fromVersion === toVersion) {
112
+ return `crew is already on version ${toVersion}`;
113
+ }
114
+ return `Upgraded crew from ${fromVersion} to ${toVersion}`;
115
+ }
91
116
  export async function createDefaultUpgradeCliOptions(args) {
92
117
  return {
93
118
  packageName: args.packageName,
@@ -104,7 +129,31 @@ export async function createDefaultUpgradeCliOptions(args) {
104
129
  },
105
130
  runInstall: async (options) => await runNpmInstallGlobal({
106
131
  ...options,
107
- spawner: createDefaultNpmSpawner(process.stderr),
132
+ spawner: createDefaultNpmSpawner(),
108
133
  }),
134
+ readInstalledVersion: readInstalledVersionFromDisk,
109
135
  };
110
136
  }
137
+ function readInstalledVersionFromDisk(installPath) {
138
+ let raw;
139
+ try {
140
+ raw = readFileSync(join(installPath, "package.json"), "utf8");
141
+ }
142
+ catch {
143
+ return undefined;
144
+ }
145
+ let parsed;
146
+ try {
147
+ parsed = JSON.parse(raw);
148
+ }
149
+ catch {
150
+ return undefined;
151
+ }
152
+ if (typeof parsed === "object" &&
153
+ parsed !== null &&
154
+ "version" in parsed &&
155
+ typeof parsed.version === "string") {
156
+ return parsed.version;
157
+ }
158
+ return undefined;
159
+ }
@@ -7,12 +7,13 @@ export interface ClassifyInstallOptions {
7
7
  export declare function classifyInstall(options: ClassifyInstallOptions): InstallKind;
8
8
  export interface NpmSpawnerResult {
9
9
  exitCode: number;
10
- stderrText: string;
10
+ outputText: string;
11
11
  }
12
12
  export type NpmSpawner = (command: string, args: readonly string[]) => Promise<NpmSpawnerResult>;
13
13
  export interface NpmRunResult {
14
14
  exitCode: number;
15
15
  sawEacces: boolean;
16
+ outputText: string;
16
17
  }
17
18
  export interface RunNpmInstallOptions {
18
19
  packageName: string;
@@ -25,5 +26,5 @@ export declare function detectInstallPath(cliMetaUrl: string): string;
25
26
  export type NpmRootRunner = (command: string, args: readonly string[]) => string;
26
27
  export declare function detectNpmRootGlobal(npmBin: string, runner: NpmRootRunner): string | undefined;
27
28
  export declare function detectIsSymlink(path: string): boolean;
28
- export declare function createDefaultNpmSpawner(passthroughStderr: NodeJS.WritableStream): NpmSpawner;
29
+ export declare function createDefaultNpmSpawner(): NpmSpawner;
29
30
  //# sourceMappingURL=npmGlobal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"npmGlobal.d.ts","sourceRoot":"","sources":["../../src/lib/npmGlobal.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CAY5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAO9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAEjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAM7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,wBAAgB,uBAAuB,CAAC,iBAAiB,EAAE,MAAM,CAAC,cAAc,GAAG,UAAU,CAmB5F"}
1
+ {"version":3,"file":"npmGlobal.d.ts","sourceRoot":"","sources":["../../src/lib/npmGlobal.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CAY5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,CAAC,CAQ9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAEjF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAM7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,wBAAgB,uBAAuB,IAAI,UAAU,CAmBpD"}
@@ -20,7 +20,8 @@ export async function runNpmInstallGlobal(options) {
20
20
  const result = await options.spawner(options.npmBin, args);
21
21
  return {
22
22
  exitCode: result.exitCode,
23
- sawEacces: result.stderrText.includes("EACCES"),
23
+ sawEacces: result.outputText.includes("EACCES"),
24
+ outputText: result.outputText,
24
25
  };
25
26
  }
26
27
  export function detectInstallPath(cliMetaUrl) {
@@ -42,20 +43,20 @@ export function detectIsSymlink(path) {
42
43
  return false;
43
44
  }
44
45
  }
45
- export function createDefaultNpmSpawner(passthroughStderr) {
46
+ export function createDefaultNpmSpawner() {
46
47
  return async (command, args) => await new Promise((resolve, reject) => {
47
- const child = spawn(command, [...args], { stdio: ["inherit", "inherit", "pipe"] });
48
- const { stderr } = child;
48
+ const child = spawn(command, [...args], { stdio: ["inherit", "pipe", "pipe"] });
49
49
  const chunks = [];
50
- stderr.on("data", (chunk) => {
50
+ const collect = (chunk) => {
51
51
  chunks.push(chunk);
52
- passthroughStderr.write(chunk);
53
- });
52
+ };
53
+ child.stdout.on("data", collect);
54
+ child.stderr.on("data", collect);
54
55
  child.on("error", reject);
55
56
  child.on("close", (code) => {
56
57
  resolve({
57
58
  exitCode: code ?? 1,
58
- stderrText: Buffer.concat(chunks).toString("utf8"),
59
+ outputText: Buffer.concat(chunks).toString("utf8"),
59
60
  });
60
61
  });
61
62
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
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",