compound-agent 1.3.2 → 1.3.3
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 +7 -1
- package/dist/cli.js +245 -136
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,11 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.3.3] - 2026-02-21
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Banner audio**: Rewritten as vaporwave composition with PolyBLEP anti-aliased sawtooth synthesis, biquad filters, Schroeder reverb, and delay. E minor ambiguity resolves to E major at bloom, synced 300ms ahead of the visual climax (neural brain lighting up). Post-animation reverb tail holds 1.8s for natural dissolution.
|
|
17
|
+
|
|
12
18
|
## [1.3.2] - 2026-02-21
|
|
13
19
|
|
|
14
20
|
### Added
|
|
15
21
|
|
|
16
|
-
- **Banner audio**: Pure TypeScript WAV synthesis
|
|
22
|
+
- **Banner audio**: Pure TypeScript WAV synthesis during the tendril animation. Cross-platform: `afplay` (macOS), `aplay` (Linux), PowerShell (Windows). Silently skips if player unavailable. Zero dependencies.
|
|
17
23
|
- **Test coverage**: 19 new tests for `ca about` command, changelog extraction/escaping, and `--update` doc migration path
|
|
18
24
|
|
|
19
25
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -1135,114 +1135,254 @@ function registerCompoundCommands(program2) {
|
|
|
1135
1135
|
var _require = createRequire(import.meta.url);
|
|
1136
1136
|
var _pkg = _require("../package.json");
|
|
1137
1137
|
var VERSION = _pkg.version;
|
|
1138
|
-
var
|
|
1139
|
-
var
|
|
1140
|
-
var
|
|
1141
|
-
var
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1138
|
+
var SR = 44100;
|
|
1139
|
+
var MAX_AMP = 28672;
|
|
1140
|
+
var E2 = 82.41;
|
|
1141
|
+
var B2 = 123.47;
|
|
1142
|
+
var E3 = 164.81;
|
|
1143
|
+
var B3 = 246.94;
|
|
1144
|
+
var E4 = 329.63;
|
|
1145
|
+
var Fs4 = 369.99;
|
|
1146
|
+
var Gs4 = 415.3;
|
|
1147
|
+
var B4 = 493.88;
|
|
1148
|
+
function polyBlep(phase, dt) {
|
|
1149
|
+
if (phase < dt) {
|
|
1150
|
+
const t = phase / dt;
|
|
1151
|
+
return t + t - t * t - 1;
|
|
1152
|
+
}
|
|
1153
|
+
if (phase > 1 - dt) {
|
|
1154
|
+
const t = (phase - 1) / dt;
|
|
1155
|
+
return t * t + t + t + 1;
|
|
1156
|
+
}
|
|
1157
|
+
return 0;
|
|
1158
|
+
}
|
|
1159
|
+
function sawBL(freq, t, offset = 0) {
|
|
1160
|
+
const dt = freq / SR;
|
|
1161
|
+
const phase = ((freq * t + offset) % 1 + 1) % 1;
|
|
1162
|
+
return 2 * phase - 1 - polyBlep(phase, dt);
|
|
1163
|
+
}
|
|
1164
|
+
function wideSaw(freq, t, cents = 10) {
|
|
1165
|
+
const r = Math.pow(2, cents / 1200);
|
|
1166
|
+
return (sawBL(freq, t, 0) + sawBL(freq * r, t, 0.33) + sawBL(freq / r, t, 0.66)) / 3;
|
|
1167
|
+
}
|
|
1168
|
+
var BiquadLPF = class {
|
|
1169
|
+
constructor(Q = 0.707) {
|
|
1170
|
+
this.Q = Q;
|
|
1171
|
+
}
|
|
1172
|
+
x1 = 0;
|
|
1173
|
+
x2 = 0;
|
|
1174
|
+
y1 = 0;
|
|
1175
|
+
y2 = 0;
|
|
1176
|
+
b0 = 0;
|
|
1177
|
+
b1 = 0;
|
|
1178
|
+
b2 = 0;
|
|
1179
|
+
a1 = 0;
|
|
1180
|
+
a2 = 0;
|
|
1181
|
+
lastCut = -1;
|
|
1182
|
+
process(x, cutoff) {
|
|
1183
|
+
if (cutoff !== this.lastCut) {
|
|
1184
|
+
this.lastCut = cutoff;
|
|
1185
|
+
const w0 = 2 * Math.PI * Math.min(cutoff, SR * 0.45) / SR;
|
|
1186
|
+
const sin0 = Math.sin(w0), cos0 = Math.cos(w0);
|
|
1187
|
+
const alpha = sin0 / (2 * this.Q);
|
|
1188
|
+
const a0 = 1 + alpha;
|
|
1189
|
+
this.b0 = (1 - cos0) / 2 / a0;
|
|
1190
|
+
this.b1 = (1 - cos0) / a0;
|
|
1191
|
+
this.b2 = this.b0;
|
|
1192
|
+
this.a1 = -2 * cos0 / a0;
|
|
1193
|
+
this.a2 = (1 - alpha) / a0;
|
|
1194
|
+
}
|
|
1195
|
+
const y = this.b0 * x + this.b1 * this.x1 + this.b2 * this.x2 - this.a1 * this.y1 - this.a2 * this.y2;
|
|
1196
|
+
this.x2 = this.x1;
|
|
1197
|
+
this.x1 = x;
|
|
1198
|
+
this.y2 = this.y1;
|
|
1199
|
+
this.y1 = y;
|
|
1200
|
+
return y;
|
|
1201
|
+
}
|
|
1152
1202
|
};
|
|
1153
|
-
function
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1158
|
-
function tone(freq, durationMs, amp = 1) {
|
|
1159
|
-
const samples = Math.floor(SAMPLE_RATE * durationMs / 1e3);
|
|
1160
|
-
const attack = Math.min(Math.floor(samples * 0.05), 200);
|
|
1161
|
-
const release = Math.min(Math.floor(samples * 0.15), 400);
|
|
1203
|
+
function applyDelay(samples, ms, fb, wet, lpHz = 3e3) {
|
|
1204
|
+
const len = Math.floor(SR * ms / 1e3);
|
|
1205
|
+
const buf = new Float64Array(len);
|
|
1206
|
+
const lpf = new BiquadLPF();
|
|
1162
1207
|
const out2 = [];
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1208
|
+
let i = 0;
|
|
1209
|
+
for (const s of samples) {
|
|
1210
|
+
const d = buf[i];
|
|
1211
|
+
buf[i] = s + lpf.process(d, lpHz) * fb;
|
|
1212
|
+
i = (i + 1) % len;
|
|
1213
|
+
out2.push(s + d * wet);
|
|
1166
1214
|
}
|
|
1167
1215
|
return out2;
|
|
1168
1216
|
}
|
|
1169
|
-
function
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1217
|
+
function applyReverb(samples, decayMs, wet) {
|
|
1218
|
+
const combDs = [1116, 1188, 1277, 1356, 1422, 1491];
|
|
1219
|
+
const apDs = [225, 341, 556];
|
|
1220
|
+
function comb(input, delay, decay) {
|
|
1221
|
+
const fb = Math.pow(1e-3, delay / (decay * SR / 1e3));
|
|
1222
|
+
const buf = new Float64Array(delay);
|
|
1223
|
+
const damp = new BiquadLPF(0.5);
|
|
1224
|
+
const out3 = [];
|
|
1225
|
+
let i = 0;
|
|
1226
|
+
for (const s of input) {
|
|
1227
|
+
const d = buf[i];
|
|
1228
|
+
buf[i] = s + damp.process(d, 4500) * fb;
|
|
1229
|
+
i = (i + 1) % delay;
|
|
1230
|
+
out3.push(d);
|
|
1231
|
+
}
|
|
1232
|
+
return out3;
|
|
1233
|
+
}
|
|
1234
|
+
function allpass(input, delay, c = 0.5) {
|
|
1235
|
+
const buf = new Float64Array(delay);
|
|
1236
|
+
const out3 = [];
|
|
1237
|
+
let i = 0;
|
|
1238
|
+
for (const s of input) {
|
|
1239
|
+
const d = buf[i];
|
|
1240
|
+
const v = s + d * c;
|
|
1241
|
+
buf[i] = v;
|
|
1242
|
+
i = (i + 1) % delay;
|
|
1243
|
+
out3.push(d - v * c);
|
|
1244
|
+
}
|
|
1245
|
+
return out3;
|
|
1246
|
+
}
|
|
1247
|
+
const pre = Math.floor(SR * 0.04);
|
|
1248
|
+
const delayed = new Array(pre).fill(0);
|
|
1249
|
+
for (const s of samples) delayed.push(s);
|
|
1250
|
+
const combs = combDs.map((d) => comb(delayed, d, decayMs));
|
|
1251
|
+
const sum = new Array(delayed.length).fill(0);
|
|
1252
|
+
for (const c of combs)
|
|
1253
|
+
for (let i = 0; i < sum.length; i++) sum[i] += (c[i] ?? 0) / combDs.length;
|
|
1254
|
+
let ap = sum;
|
|
1255
|
+
for (const d of apDs) ap = allpass(ap, d);
|
|
1256
|
+
let hpY = 0;
|
|
1257
|
+
const hpAlpha = 1 / SR / (1 / (2 * Math.PI * 120) + 1 / SR);
|
|
1258
|
+
const clean = [];
|
|
1259
|
+
for (const s of ap) {
|
|
1260
|
+
hpY += hpAlpha * (s - hpY);
|
|
1261
|
+
clean.push(s - hpY);
|
|
1262
|
+
}
|
|
1263
|
+
const outLen = Math.max(samples.length, clean.length);
|
|
1172
1264
|
const out2 = [];
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
out2.push(sum * scale);
|
|
1265
|
+
for (let i = 0; i < outLen; i++) {
|
|
1266
|
+
const dry = i < samples.length ? samples[i] : 0;
|
|
1267
|
+
const w = i < clean.length ? clean[i] : 0;
|
|
1268
|
+
out2.push(dry * (1 - wet * 0.3) + w * wet);
|
|
1178
1269
|
}
|
|
1179
1270
|
return out2;
|
|
1180
1271
|
}
|
|
1181
|
-
function
|
|
1182
|
-
return
|
|
1183
|
-
}
|
|
1184
|
-
function
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
const
|
|
1226
|
-
const
|
|
1227
|
-
const
|
|
1272
|
+
function expCurve(x, pow = 2) {
|
|
1273
|
+
return Math.pow(Math.max(0, Math.min(1, x)), pow);
|
|
1274
|
+
}
|
|
1275
|
+
function saturate(x) {
|
|
1276
|
+
return Math.tanh(x * 1.15) / Math.tanh(1.15);
|
|
1277
|
+
}
|
|
1278
|
+
function voiceRoot(i, t, f) {
|
|
1279
|
+
const atk = i < SR * 8e-3 ? i / (SR * 8e-3) : 1;
|
|
1280
|
+
const dec = Math.exp(-t * 2.5);
|
|
1281
|
+
const amp = atk * dec * 0.35;
|
|
1282
|
+
if (amp < 1e-3) return 0;
|
|
1283
|
+
const s = wideSaw(E2, t, 6) * 0.7 + Math.sin(2 * Math.PI * B2 * t) * 0.3;
|
|
1284
|
+
return f.process(s, 800 + dec * 1200) * amp;
|
|
1285
|
+
}
|
|
1286
|
+
function voicePad(t, fE, fB) {
|
|
1287
|
+
if (t < 1) return 0;
|
|
1288
|
+
const padT = t - 1;
|
|
1289
|
+
const fadeIn = expCurve(Math.min(1, padT / 1.8));
|
|
1290
|
+
const fadeOut = t < 4 ? 1 : expCurve(1 - (t - 4) / 3.5, 1.5);
|
|
1291
|
+
const amp = fadeIn * fadeOut * 0.2;
|
|
1292
|
+
if (amp < 1e-3) return 0;
|
|
1293
|
+
let cutoff;
|
|
1294
|
+
if (t < 2.7) cutoff = 350 + padT / 1.7 * 850;
|
|
1295
|
+
else if (t < 3.1) cutoff = 1200 + expCurve((t - 2.7) / 0.4) * 2800;
|
|
1296
|
+
else cutoff = 4e3 - expCurve((t - 3.1) / 4, 1.3) * 3200;
|
|
1297
|
+
cutoff *= 1 + 0.04 * Math.sin(2 * Math.PI * 0.11 * t);
|
|
1298
|
+
const dE = 1 + 8e-4 * Math.sin(2 * Math.PI * 0.09 * t);
|
|
1299
|
+
const dB = 1 + 7e-4 * Math.sin(2 * Math.PI * 0.12 * t + 0.5);
|
|
1300
|
+
const e3 = fE.process(wideSaw(E3 * dE, t, 12), cutoff);
|
|
1301
|
+
const b3 = fB.process(wideSaw(B3 * dB, t, 12), cutoff);
|
|
1302
|
+
return (e3 * 0.55 + b3 * 0.45) * amp;
|
|
1303
|
+
}
|
|
1304
|
+
function voiceForeshadow(t, f) {
|
|
1305
|
+
if (t < 2.3 || t >= 3.5) return 0;
|
|
1306
|
+
const fT = t - 2.3;
|
|
1307
|
+
const fadeIn = expCurve(Math.min(1, fT / 0.7));
|
|
1308
|
+
const fadeOut = fT > 0.7 ? expCurve((1.2 - fT) / 0.5) : 1;
|
|
1309
|
+
const amp = fadeIn * fadeOut * 0.07;
|
|
1310
|
+
if (amp < 1e-3) return 0;
|
|
1311
|
+
return f.process(Math.sin(2 * Math.PI * Fs4 * t), 3e3) * amp;
|
|
1312
|
+
}
|
|
1313
|
+
function voiceBloom(t, fE, fG, fB) {
|
|
1314
|
+
if (t < 2.9) return 0;
|
|
1315
|
+
const bT = t - 2.9;
|
|
1316
|
+
const atk = Math.min(1, bT / 0.08);
|
|
1317
|
+
const sus = bT < 1.5 ? 1 : Math.exp(-(bT - 1.5) * 0.8);
|
|
1318
|
+
const amp = atk * sus;
|
|
1319
|
+
if (amp < 2e-3) return 0;
|
|
1320
|
+
const bloomF = bT < 0.3 ? 1500 + expCurve(bT / 0.3) * 3500 : 5e3 - expCurve(Math.min(1, (bT - 0.3) / 4), 1.2) * 3500;
|
|
1321
|
+
const dE4 = 1 + 5e-4 * Math.sin(2 * Math.PI * 0.08 * t);
|
|
1322
|
+
const e4 = fE.process(wideSaw(E4 * dE4, t, 8), bloomF);
|
|
1323
|
+
let gs4 = 0;
|
|
1324
|
+
if (bT > 0.03) {
|
|
1325
|
+
const gsA = Math.min(1, (bT - 0.03) / 0.1);
|
|
1326
|
+
const gsD = 1 + 6e-4 * Math.sin(2 * Math.PI * 0.1 * t + 1);
|
|
1327
|
+
gs4 = fG.process(wideSaw(Gs4 * gsD, t, 9), bloomF) * gsA;
|
|
1328
|
+
}
|
|
1329
|
+
let b4 = 0;
|
|
1330
|
+
if (bT > 0.05 && bT < 3) {
|
|
1331
|
+
const bA = Math.min(1, (bT - 0.05) / 0.15) * (bT > 2 ? expCurve(1 - (bT - 2)) : 1);
|
|
1332
|
+
b4 = fB.process(
|
|
1333
|
+
Math.sin(2 * Math.PI * B4 * t) * 0.5 + wideSaw(B4, t, 6) * 0.5,
|
|
1334
|
+
bloomF
|
|
1335
|
+
) * bA;
|
|
1336
|
+
}
|
|
1337
|
+
return (e4 * 0.35 + gs4 * 0.35 + b4 * 0.15) * amp * 0.28;
|
|
1338
|
+
}
|
|
1339
|
+
function voiceSub(t) {
|
|
1340
|
+
const subIn = expCurve(Math.min(1, t / 2));
|
|
1341
|
+
const subOut = t < 4.5 ? 1 : expCurve(1 - (t - 4.5) / 2.5, 1.5);
|
|
1342
|
+
return Math.sin(2 * Math.PI * E2 * t) * subIn * subOut * 0.08;
|
|
1343
|
+
}
|
|
1344
|
+
function compose() {
|
|
1345
|
+
const TOTAL = Math.floor(SR * 7.5);
|
|
1346
|
+
const out2 = new Float64Array(TOTAL);
|
|
1347
|
+
const f = [];
|
|
1348
|
+
for (let i = 0; i < 7; i++) f.push(new BiquadLPF(0.6));
|
|
1349
|
+
for (let i = 0; i < TOTAL; i++) {
|
|
1350
|
+
const t = i / SR;
|
|
1351
|
+
out2[i] = saturate(
|
|
1352
|
+
voiceRoot(i, t, f[0]) + voicePad(t, f[1], f[2]) + voiceForeshadow(t, f[3]) + voiceBloom(t, f[4], f[5], f[6]) + voiceSub(t)
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
let result = Array.from(out2);
|
|
1356
|
+
result = applyDelay(result, 520, 0.22, 0.18, 2800);
|
|
1357
|
+
result = applyReverb(result, 4800, 0.4);
|
|
1358
|
+
return result;
|
|
1359
|
+
}
|
|
1360
|
+
function encodeWav(raw) {
|
|
1361
|
+
let peak = 0;
|
|
1362
|
+
for (const s of raw) {
|
|
1363
|
+
const a = Math.abs(s);
|
|
1364
|
+
if (a > peak) peak = a;
|
|
1365
|
+
}
|
|
1366
|
+
const scale = peak > 0 ? 0.92 / peak : 1;
|
|
1367
|
+
const dataSize = raw.length * 2;
|
|
1368
|
+
const buf = Buffer.alloc(44 + dataSize);
|
|
1228
1369
|
buf.write("RIFF", 0);
|
|
1229
|
-
buf.writeUInt32LE(
|
|
1370
|
+
buf.writeUInt32LE(36 + dataSize, 4);
|
|
1230
1371
|
buf.write("WAVE", 8);
|
|
1231
1372
|
buf.write("fmt ", 12);
|
|
1232
1373
|
buf.writeUInt32LE(16, 16);
|
|
1233
1374
|
buf.writeUInt16LE(1, 20);
|
|
1234
1375
|
buf.writeUInt16LE(1, 22);
|
|
1235
|
-
buf.writeUInt32LE(
|
|
1236
|
-
buf.writeUInt32LE(
|
|
1237
|
-
buf.writeUInt16LE(
|
|
1238
|
-
buf.writeUInt16LE(
|
|
1376
|
+
buf.writeUInt32LE(SR, 24);
|
|
1377
|
+
buf.writeUInt32LE(SR * 2, 28);
|
|
1378
|
+
buf.writeUInt16LE(2, 32);
|
|
1379
|
+
buf.writeUInt16LE(16, 34);
|
|
1239
1380
|
buf.write("data", 36);
|
|
1240
1381
|
buf.writeUInt32LE(dataSize, 40);
|
|
1241
|
-
let
|
|
1242
|
-
for (const s of
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
offset += 2;
|
|
1382
|
+
let off = 44;
|
|
1383
|
+
for (const s of raw) {
|
|
1384
|
+
buf.writeInt16LE(Math.round(Math.max(-1, Math.min(1, s * scale)) * MAX_AMP), off);
|
|
1385
|
+
off += 2;
|
|
1246
1386
|
}
|
|
1247
1387
|
return buf;
|
|
1248
1388
|
}
|
|
@@ -1267,7 +1407,7 @@ function spawnPlayer(filePath) {
|
|
|
1267
1407
|
}
|
|
1268
1408
|
function playBannerAudio() {
|
|
1269
1409
|
try {
|
|
1270
|
-
const wav = encodeWav(
|
|
1410
|
+
const wav = encodeWav(compose());
|
|
1271
1411
|
const tmpPath = join(tmpdir(), `ca-banner-${process.pid}.wav`);
|
|
1272
1412
|
writeFileSync(tmpPath, wav);
|
|
1273
1413
|
const proc = spawnPlayer(tmpPath);
|
|
@@ -1639,10 +1779,13 @@ async function playInstallBanner() {
|
|
|
1639
1779
|
await sleep(120);
|
|
1640
1780
|
}
|
|
1641
1781
|
} finally {
|
|
1642
|
-
audio?.stop();
|
|
1643
1782
|
process.removeListener("exit", restoreCursor);
|
|
1644
1783
|
restoreCursor();
|
|
1645
1784
|
write("\n");
|
|
1785
|
+
if (audio) {
|
|
1786
|
+
await sleep(1800);
|
|
1787
|
+
audio.stop();
|
|
1788
|
+
}
|
|
1646
1789
|
}
|
|
1647
1790
|
}
|
|
1648
1791
|
function checkBeadsAvailable() {
|
|
@@ -7532,11 +7675,17 @@ function registerVerifyGatesCommand(program2) {
|
|
|
7532
7675
|
}
|
|
7533
7676
|
|
|
7534
7677
|
// src/changelog-data.ts
|
|
7535
|
-
var CHANGELOG_RECENT = `## [1.3.
|
|
7678
|
+
var CHANGELOG_RECENT = `## [1.3.3] - 2026-02-21
|
|
7679
|
+
|
|
7680
|
+
### Changed
|
|
7681
|
+
|
|
7682
|
+
- **Banner audio**: Rewritten as vaporwave composition with PolyBLEP anti-aliased sawtooth synthesis, biquad filters, Schroeder reverb, and delay. E minor ambiguity resolves to E major at bloom, synced 300ms ahead of the visual climax (neural brain lighting up). Post-animation reverb tail holds 1.8s for natural dissolution.
|
|
7683
|
+
|
|
7684
|
+
## [1.3.2] - 2026-02-21
|
|
7536
7685
|
|
|
7537
7686
|
### Added
|
|
7538
7687
|
|
|
7539
|
-
- **Banner audio**: Pure TypeScript WAV synthesis
|
|
7688
|
+
- **Banner audio**: Pure TypeScript WAV synthesis during the tendril animation. Cross-platform: \`afplay\` (macOS), \`aplay\` (Linux), PowerShell (Windows). Silently skips if player unavailable. Zero dependencies.
|
|
7540
7689
|
- **Test coverage**: 19 new tests for \`ca about\` command, changelog extraction/escaping, and \`--update\` doc migration path
|
|
7541
7690
|
|
|
7542
7691
|
### Fixed
|
|
@@ -7566,47 +7715,7 @@ var CHANGELOG_RECENT = `## [1.3.2] - 2026-02-21
|
|
|
7566
7715
|
- **Test-cleaner Phase 3 strengthened**: Adversarial review phase now mandates iteration loop until both reviewers give unconditional approval. Heading, emphasis, and quality criteria updated.
|
|
7567
7716
|
- **Update hint on upgrade**: When \`ca init\` or \`ca setup\` detects an existing install, displays tip to run with \`--update\` to regenerate managed files
|
|
7568
7717
|
- **HOW_TO_COMPOUND.md migration**: \`ca setup --update\` automatically removes old monolithic \`HOW_TO_COMPOUND.md\` if it has version frontmatter (generated by compound-agent)
|
|
7569
|
-
- **Doctor doc check**: Now checks for \`docs/compound/README.md\` instead of \`HOW_TO_COMPOUND.md
|
|
7570
|
-
|
|
7571
|
-
## [1.3.0] - 2026-02-21
|
|
7572
|
-
|
|
7573
|
-
### Added
|
|
7574
|
-
|
|
7575
|
-
- **Setup hardening**: Four new pre-flight checks during \`ca init\` and \`ca setup\`:
|
|
7576
|
-
- **Beads CLI check** (\`beads-check.ts\`): Detects if \`bd\` is available, shows install URL if missing (informational, non-blocking)
|
|
7577
|
-
- **User-scope detection** (\`scope-check.ts\`): Warns when installing at home directory level where lessons are shared across projects
|
|
7578
|
-
- **.gitignore injection** (\`gitignore.ts\`): Ensures \`node_modules/\` and \`.claude/.cache/\` patterns exist in \`.gitignore\`
|
|
7579
|
-
- **Upgrade detection** (\`upgrade.ts\`): Detects existing installs and runs migration pipeline (deprecated command removal, header stripping, doc version update)
|
|
7580
|
-
- **Upgrade engine**: Automated migration from v1.2.x to v1.3.0:
|
|
7581
|
-
- Removes 5 deprecated CLI wrapper commands (\`search.md\`, \`list.md\`, \`show.md\`, \`stats.md\`, \`wrong.md\`)
|
|
7582
|
-
- Strips legacy \`<!-- generated by compound-agent -->\` headers from installed files
|
|
7583
|
-
- Updates \`HOW_TO_COMPOUND.md\` version during upgrade
|
|
7584
|
-
- **\`/compound:research\` skill**: PhD-depth research producing structured survey documents following \`TEMPLATE_FOR_RESEARCH.md\` format
|
|
7585
|
-
- **\`/compound:test-clean\` skill**: 5-phase test suite optimization with adversarial review (audit, design, implement, verify, report)
|
|
7586
|
-
- **Documentation template**: \`HOW_TO_COMPOUND.md\` deployed to \`docs/compound/\` during setup with version and date placeholders
|
|
7587
|
-
- **Test scripts**: \`test:segment\` (run tests for specific module), \`test:random\` (seeded random subset), \`test:critical\` (*.critical.test.ts convention)
|
|
7588
|
-
- **3 new doctor checks**: Beads CLI availability, \`.gitignore\` health, usage documentation presence
|
|
7589
|
-
|
|
7590
|
-
### Fixed
|
|
7591
|
-
|
|
7592
|
-
- **\`setup --update --dry-run\` no longer mutates files**: \`runUpgrade()\` now accepts a \`dryRun\` parameter propagated to all sub-functions (removeDeprecatedCommands, stripGeneratedHeaders, upgradeDocVersion)
|
|
7593
|
-
- **\`setup --uninstall\` respects plugin.json ownership**: Checks \`name === "compound-agent"\` before deleting; user-owned plugin manifests are preserved
|
|
7594
|
-
- **Upgrade ownership guard**: \`removeDeprecatedCommands\` checks file content for compound-agent markers before deleting, preventing silent removal of user-authored files with the same name
|
|
7595
|
-
- **Malformed settings.json no longer silently clobbered**: On parse error, \`configureClaudeSettings\` warns and skips instead of overwriting with empty config
|
|
7596
|
-
|
|
7597
|
-
### Changed
|
|
7598
|
-
|
|
7599
|
-
- **\`setup --update\` overhaul**: Now uses path-based file detection (compound/ = managed) instead of marker-based. Runs upgrade pipeline and \`.gitignore\` remediation during update
|
|
7600
|
-
- **JSON-first \`bd\` parsing in loop**: \`jq\` primary with \`python3\` fallback via \`parse_json()\` helper
|
|
7601
|
-
- **CLI test helpers hardened**: Replaced shell string interpolation with \`execFileSync\` for safety and reliability
|
|
7602
|
-
- **Beads check portable**: Uses POSIX \`command -v\` instead of non-portable \`which\`
|
|
7603
|
-
- **Template expansion**: Brainstorm and plan skills now cross-reference researcher skill; 9 total skills, 11 total commands
|
|
7604
|
-
- **Code organization**: Extracted display utilities to \`display-utils.ts\`, uninstall logic to \`uninstall.ts\`
|
|
7605
|
-
|
|
7606
|
-
### Removed
|
|
7607
|
-
|
|
7608
|
-
- **5 deprecated CLI wrapper commands**: \`search.md\`, \`list.md\`, \`show.md\`, \`stats.md\`, \`wrong.md\` (redundant wrappers around \`npx ca <cmd>\`)
|
|
7609
|
-
- **\`GENERATED_MARKER\` on new installs**: New installs use path-based detection; marker retained only for backward-compatible \`--update\` detection`;
|
|
7718
|
+
- **Doctor doc check**: Now checks for \`docs/compound/README.md\` instead of \`HOW_TO_COMPOUND.md\``;
|
|
7610
7719
|
|
|
7611
7720
|
// src/commands/about.ts
|
|
7612
7721
|
function registerAboutCommand(program2) {
|