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 +24 -0
- package/bin/ca +51 -10
- package/package.json +8 -1
- package/scripts/postinstall.cjs +34 -10
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
|
-
//
|
|
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
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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": {
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -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
|
-
|
|
63
|
-
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
console.
|
|
211
|
-
|
|
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
|
|