compound-agent 2.0.0 → 2.0.2

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 CHANGED
@@ -7,6 +7,30 @@ All notable changes to this project will be documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## [2.0.2] - 2026-03-22
11
+
12
+ ### Fixed
13
+
14
+ - **Silent empty-result messages**: `search`, `load-session`, and `check-plan` commands showed no output when no results found (missing trailing newline merged text with shell prompt).
15
+ - **Setup command output**: `setup` now shows the same detailed breakdown as `init` (directories created, template counts by type, AGENTS.md/CLAUDE.md status).
16
+ - **Beads status reporting**: `init` and `setup` now report whether beads CLI is installed and whether the repo is initialized. Shows install instructions if missing.
17
+
18
+ ## [2.0.1] - 2026-03-22
19
+
20
+ ### Changed
21
+
22
+ - **Platform-specific npm distribution**: Switch from postinstall-only binary download to industry-standard optional dependencies pattern (`@syottos/{os}-{arch}`). Works with pnpm out of the box — no `approve-builds` needed.
23
+ - **Lazy download fallback**: If platform packages are missing and postinstall was blocked, the bin wrapper downloads the binary on first use from GitHub Releases.
24
+ - **Non-fatal postinstall**: Postinstall no longer crashes `npm install` on download failure — the lazy fallback handles it.
25
+ - **npm publish provenance**: All packages published with `--provenance` for supply chain transparency.
26
+ - **Checksum verification in CI**: Release workflow verifies SHA256 checksums before publishing to npm.
27
+ - **Version-aware skip logic**: Postinstall checks binary version matches package version, preventing stale binaries after upgrades.
28
+
29
+ ### Added
30
+
31
+ - **Weekly npm token health check**: Scheduled workflow verifies `NPM_TOKEN` validity every Monday.
32
+ - **Platform publish script**: `scripts/publish-platforms.cjs` handles creating and publishing `@syottos/*` packages during releases. Idempotent — safe to re-run.
33
+
10
34
  ## [Unreleased]
11
35
 
12
36
  ### Changed
package/bin/ca CHANGED
@@ -1,32 +1,73 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Thin wrapper that spawns the Go binary.
4
- // Uses Node.js only for locating the binary; all work is done by the Go process.
4
+ // Resolution order:
5
+ // 1. CA_BINARY_PATH env override
6
+ // 2. Platform-specific npm package (@syottos/{os}-{arch})
7
+ // 3. Postinstall-downloaded binary (./ca-binary)
8
+ // 4. Local Go dev build (../go/dist/ca)
9
+ // 5. Lazy download from GitHub Releases (fallback when postinstall was blocked)
5
10
 
6
11
  import { execFileSync } from "node:child_process";
7
12
  import { existsSync } from "node:fs";
8
13
  import { resolve, dirname } from "node:path";
9
14
  import { fileURLToPath } from "node:url";
15
+ import { createRequire } from "node:module";
10
16
 
11
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const require = createRequire(import.meta.url);
12
19
 
13
- // Resolution order: env override → postinstall binary → local Go build
14
- const candidates = [
15
- process.env.CA_BINARY_PATH,
16
- resolve(__dirname, "ca-binary"),
17
- resolve(__dirname, "..", "go", "dist", "ca"),
18
- ].filter(Boolean);
20
+ function resolvePlatformBinary() {
21
+ const pkg = `@syottos/${process.platform}-${process.arch}`;
22
+ try {
23
+ const pkgDir = dirname(require.resolve(`${pkg}/package.json`));
24
+ const bin = resolve(pkgDir, "bin", "ca");
25
+ if (existsSync(bin)) return bin;
26
+ } catch {
27
+ // Platform package not installed
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function findBinary() {
33
+ const candidates = [
34
+ process.env.CA_BINARY_PATH,
35
+ resolvePlatformBinary(),
36
+ resolve(__dirname, "ca-binary"),
37
+ resolve(__dirname, "..", "go", "dist", "ca"),
38
+ ].filter(Boolean);
39
+
40
+ return candidates.find((p) => existsSync(p)) || null;
41
+ }
19
42
 
20
- const binaryPath = candidates.find((p) => existsSync(p));
43
+ let binaryPath = findBinary();
44
+
45
+ // Lazy download: if no binary found, try running the postinstall script on-the-fly.
46
+ // This handles: pnpm blocking postinstall + platform packages not published (expired token).
47
+ if (!binaryPath) {
48
+ const postinstall = resolve(__dirname, "..", "scripts", "postinstall.cjs");
49
+ if (existsSync(postinstall)) {
50
+ try {
51
+ console.error("[compound-agent] Binary not found, downloading from GitHub Releases...");
52
+ execFileSync(process.execPath, [postinstall], {
53
+ stdio: "inherit",
54
+ env: { ...process.env, npm_package_name: "" },
55
+ });
56
+ binaryPath = findBinary();
57
+ } catch {
58
+ // Download failed, fall through to error
59
+ }
60
+ }
61
+ }
21
62
 
22
63
  if (!binaryPath) {
23
- console.error("[compound-agent] Binary not found. Try reinstalling compound-agent or run: cd go && make build");
64
+ console.error("[compound-agent] Binary not found and download failed.");
65
+ console.error("[compound-agent] Try: pnpm approve-builds && pnpm install compound-agent");
24
66
  process.exit(1);
25
67
  }
26
68
 
27
69
  try {
28
70
  execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
29
71
  } catch (err) {
30
- // execFileSync throws on non-zero exit codes; forward the exit code
31
72
  process.exit(err.status || 1);
32
73
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "compound-agent",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
+ "type": "module",
4
5
  "description": "Learning system for Claude Code — avoids repeating mistakes across sessions",
5
6
  "bin": {
6
7
  "ca": "./bin/ca",
@@ -48,6 +49,12 @@
48
49
  "tdd",
49
50
  "knowledge-management"
50
51
  ],
52
+ "optionalDependencies": {
53
+ "@syottos/darwin-arm64": "2.0.2",
54
+ "@syottos/darwin-x64": "2.0.2",
55
+ "@syottos/linux-arm64": "2.0.2",
56
+ "@syottos/linux-x64": "2.0.2"
57
+ },
51
58
  "author": "Nathan Delacrétaz",
52
59
  "license": "MIT",
53
60
  "engines": {
@@ -50,7 +50,7 @@ function verifyChecksum(filePath, artifactName, checksumsPath) {
50
50
  return actualHash === expectedHash;
51
51
  }
52
52
 
53
- function shouldSkipDownload(binDir) {
53
+ function shouldSkipDownload(binDir, expectedVersion) {
54
54
  const caPath = path.join(binDir, "ca-binary");
55
55
  const embedPath = path.join(binDir, "ca-embed");
56
56
 
@@ -59,8 +59,11 @@ function shouldSkipDownload(binDir) {
59
59
  }
60
60
 
61
61
  try {
62
- // P1-2 fix: use execFileSync (no shell) instead of execSync
63
- execFileSync(caPath, ["version"], { stdio: "pipe" });
62
+ const output = execFileSync(caPath, ["version"], { stdio: "pipe", encoding: "utf-8" });
63
+ // Verify version matches to prevent stale binaries after upgrade
64
+ if (expectedVersion && !output.includes(expectedVersion)) {
65
+ return false;
66
+ }
64
67
  return true;
65
68
  } catch {
66
69
  return false;
@@ -89,13 +92,15 @@ function downloadFile(url, dest) {
89
92
  }
90
93
 
91
94
  if (res.statusCode !== 200) {
95
+ res.resume(); // Drain response to free the socket
92
96
  reject(new Error(`Download failed: HTTP ${res.statusCode} from ${currentUrl}`));
93
97
  return;
94
98
  }
95
99
 
96
100
  const file = fs.createWriteStream(dest);
97
101
  res.pipe(file);
98
- file.on("finish", resolve);
102
+ file.on("finish", () => file.close());
103
+ file.on("close", resolve);
99
104
  file.on("error", (err) => {
100
105
  fs.unlink(dest, () => {});
101
106
  reject(err);
@@ -133,10 +138,26 @@ function cleanupBinaries(binDir) {
133
138
  }
134
139
  }
135
140
 
141
+ function platformPackageInstalled() {
142
+ const pkg = `@syottos/${require("os").platform()}-${require("os").arch()}`;
143
+ try {
144
+ const pkgDir = path.dirname(require.resolve(`${pkg}/package.json`));
145
+ return fs.existsSync(path.join(pkgDir, "bin", "ca"));
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
136
151
  async function main() {
137
152
  // Skip self-install (when running pnpm install inside compound-agent itself)
138
153
  if (process.env.npm_package_name === "compound-agent") return;
139
154
 
155
+ // Skip if platform-specific package already provides the binary
156
+ if (platformPackageInstalled()) {
157
+ console.log("[compound-agent] Binary provided by platform package, skipping download");
158
+ return;
159
+ }
160
+
140
161
  const platformKey = getPlatformKey(
141
162
  require("os").platform(),
142
163
  require("os").arch()
@@ -147,7 +168,7 @@ async function main() {
147
168
 
148
169
  const binDir = path.resolve(__dirname, "../bin");
149
170
 
150
- if (shouldSkipDownload(binDir)) {
171
+ if (shouldSkipDownload(binDir, version)) {
151
172
  console.log("[compound-agent] Binaries already installed, skipping download");
152
173
  return;
153
174
  }
@@ -168,7 +189,9 @@ async function main() {
168
189
 
169
190
  // Download both binaries in parallel (to .tmp names)
170
191
  const caArtifact = `ca-${platformKey}`;
171
- const embedArtifact = `ca-embed-${platformKey}`;
192
+ // No x86_64 macOS embed build (ort-sys limitation) — use arm64 via Rosetta
193
+ const embedKey = platformKey === "darwin-amd64" ? "darwin-arm64" : platformKey;
194
+ const embedArtifact = `ca-embed-${embedKey}`;
172
195
 
173
196
  await Promise.all([
174
197
  downloadBinary(binDir, `${baseUrl}/${caArtifact}`, "ca-binary", "CLI binary"),
@@ -205,10 +228,11 @@ async function main() {
205
228
  throw new Error("Binary downloaded but functional check failed (ca version exited non-zero)");
206
229
  }
207
230
  } catch (err) {
208
- console.error(`[compound-agent] Installation failed: ${err.message}`);
209
- console.error("[compound-agent] You can manually download binaries from:");
210
- console.error(`[compound-agent] https://github.com/${REPO}/releases/tag/v${version}`);
211
- process.exit(1);
231
+ // Non-fatal: the bin/ca wrapper will retry via lazy download on first run.
232
+ // Exiting 0 so npm/pnpm install doesn't fail.
233
+ console.warn(`[compound-agent] Postinstall download failed: ${err.message}`);
234
+ console.warn("[compound-agent] The binary will be downloaded on first use.");
235
+ console.warn(`[compound-agent] Or manually download from: https://github.com/${REPO}/releases/tag/v${version}`);
212
236
  }
213
237
  }
214
238