compound-agent 1.3.1 → 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 +26 -1
- package/README.md +1 -1
- package/dist/cli.js +341 -68
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,11 +9,36 @@ 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
|
+
|
|
18
|
+
## [1.3.2] - 2026-02-21
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
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.
|
|
23
|
+
- **Test coverage**: 19 new tests for `ca about` command, changelog extraction/escaping, and `--update` doc migration path
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **`setup --update` doc migration**: `--update` now installs the 5 split docs before removing legacy `HOW_TO_COMPOUND.md`, preventing empty `docs/compound/`
|
|
28
|
+
- **Fresh checkout type-check**: `src/changelog-data.ts` tracked in git so `tsc --noEmit` passes without a prior build
|
|
29
|
+
- **Trailing status text**: Banner animation no longer leaves "al tendrils..." remnant from previous phase
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **`ca about` command**: Renamed from `ca version-show` for brevity
|
|
34
|
+
- **Changelog extraction**: Core parsing/escaping logic extracted to `scripts/changelog-utils.ts` (shared between prebuild script and tests)
|
|
35
|
+
- **Narrowed `.gitignore`**: Setup-generated patterns scoped to `compound/` subdirectories to avoid hiding tracked TDD agent definitions
|
|
36
|
+
|
|
12
37
|
## [1.3.1] - 2026-02-21
|
|
13
38
|
|
|
14
39
|
### Added
|
|
15
40
|
|
|
16
|
-
- **`ca
|
|
41
|
+
- **`ca about` command**: Displays version with terminal animation (tendril growth) and recent changelog entries. Non-TTY environments get plain text output. Changelog is embedded at build time from CHANGELOG.md.
|
|
17
42
|
- **3 new doctor checks**: Beads initialized (`.beads/` dir), beads healthy (`bd doctor`), codebase scope (user-scope detection)
|
|
18
43
|
- **Beads + scope status in init/setup output**: Full beads health display (CLI available, initialized, healthy) and scope status shown after `ca init`, `ca setup`, and `ca setup --update`
|
|
19
44
|
- **Banner on `--update`**: Terminal art animation now plays during `ca setup --update` and `ca init --update` (same TTY/quiet guards as fresh install)
|
package/README.md
CHANGED
|
@@ -210,7 +210,7 @@ Generated scripts detect three markers: `EPIC_COMPLETE` (success), `EPIC_FAILED`
|
|
|
210
210
|
| `ca setup claude --status` | Check Claude Code integration health |
|
|
211
211
|
| `ca setup claude --uninstall` | Remove Claude hooks only |
|
|
212
212
|
| `ca download-model` | Download the embedding model |
|
|
213
|
-
| `ca
|
|
213
|
+
| `ca about` | Show version, animation, and recent changelog |
|
|
214
214
|
| `ca doctor` | Verify external dependencies and project health |
|
|
215
215
|
|
|
216
216
|
## Workflow Commands
|
package/dist/cli.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { getLlama, resolveModelFile } from 'node-llama-cpp';
|
|
4
4
|
import { mkdirSync, writeFileSync, statSync, unlinkSync, existsSync, readFileSync, copyFileSync, chmodSync, readdirSync } from 'fs';
|
|
5
|
-
import { homedir } from 'os';
|
|
5
|
+
import { homedir, tmpdir } from 'os';
|
|
6
6
|
import path, { join, dirname, resolve, relative } from 'path';
|
|
7
7
|
import * as fs from 'fs/promises';
|
|
8
8
|
import { readFile, mkdir, appendFile, writeFile, chmod, rm, rename, readdir } from 'fs/promises';
|
|
9
9
|
import { createHash } from 'crypto';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
|
-
import { execSync, execFileSync } from 'child_process';
|
|
12
|
+
import { execSync, execFileSync, spawn } from 'child_process';
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
|
|
15
15
|
// src/cli-utils.ts
|
|
@@ -1135,6 +1135,311 @@ 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 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
|
+
}
|
|
1202
|
+
};
|
|
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();
|
|
1207
|
+
const out2 = [];
|
|
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);
|
|
1214
|
+
}
|
|
1215
|
+
return out2;
|
|
1216
|
+
}
|
|
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);
|
|
1264
|
+
const out2 = [];
|
|
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);
|
|
1269
|
+
}
|
|
1270
|
+
return out2;
|
|
1271
|
+
}
|
|
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);
|
|
1369
|
+
buf.write("RIFF", 0);
|
|
1370
|
+
buf.writeUInt32LE(36 + dataSize, 4);
|
|
1371
|
+
buf.write("WAVE", 8);
|
|
1372
|
+
buf.write("fmt ", 12);
|
|
1373
|
+
buf.writeUInt32LE(16, 16);
|
|
1374
|
+
buf.writeUInt16LE(1, 20);
|
|
1375
|
+
buf.writeUInt16LE(1, 22);
|
|
1376
|
+
buf.writeUInt32LE(SR, 24);
|
|
1377
|
+
buf.writeUInt32LE(SR * 2, 28);
|
|
1378
|
+
buf.writeUInt16LE(2, 32);
|
|
1379
|
+
buf.writeUInt16LE(16, 34);
|
|
1380
|
+
buf.write("data", 36);
|
|
1381
|
+
buf.writeUInt32LE(dataSize, 40);
|
|
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;
|
|
1386
|
+
}
|
|
1387
|
+
return buf;
|
|
1388
|
+
}
|
|
1389
|
+
function spawnPlayer(filePath) {
|
|
1390
|
+
try {
|
|
1391
|
+
switch (process.platform) {
|
|
1392
|
+
case "darwin":
|
|
1393
|
+
return spawn("afplay", [filePath], { stdio: "ignore", detached: true });
|
|
1394
|
+
case "linux":
|
|
1395
|
+
return spawn("aplay", ["-q", filePath], { stdio: "ignore", detached: true });
|
|
1396
|
+
case "win32":
|
|
1397
|
+
return spawn("powershell", [
|
|
1398
|
+
"-c",
|
|
1399
|
+
`(New-Object Media.SoundPlayer '${filePath}').PlaySync()`
|
|
1400
|
+
], { stdio: "ignore", detached: true });
|
|
1401
|
+
default:
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
} catch {
|
|
1405
|
+
return null;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
function playBannerAudio() {
|
|
1409
|
+
try {
|
|
1410
|
+
const wav = encodeWav(compose());
|
|
1411
|
+
const tmpPath = join(tmpdir(), `ca-banner-${process.pid}.wav`);
|
|
1412
|
+
writeFileSync(tmpPath, wav);
|
|
1413
|
+
const proc = spawnPlayer(tmpPath);
|
|
1414
|
+
if (!proc) {
|
|
1415
|
+
try {
|
|
1416
|
+
unlinkSync(tmpPath);
|
|
1417
|
+
} catch {
|
|
1418
|
+
}
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
proc.unref();
|
|
1422
|
+
const cleanup2 = () => {
|
|
1423
|
+
try {
|
|
1424
|
+
proc.kill();
|
|
1425
|
+
} catch {
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
unlinkSync(tmpPath);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
proc.on("exit", () => {
|
|
1433
|
+
try {
|
|
1434
|
+
unlinkSync(tmpPath);
|
|
1435
|
+
} catch {
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
return { stop: cleanup2 };
|
|
1439
|
+
} catch {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1138
1443
|
|
|
1139
1444
|
// src/setup/banner.ts
|
|
1140
1445
|
var W = 62;
|
|
@@ -1316,6 +1621,7 @@ async function playInstallBanner() {
|
|
|
1316
1621
|
const ps = [];
|
|
1317
1622
|
const write = (s) => process.stdout.write(s);
|
|
1318
1623
|
write("\x1B[?25l\x1B[2J\x1B[H");
|
|
1624
|
+
const audio = playBannerAudio();
|
|
1319
1625
|
const restoreCursor = () => process.stdout.write("\x1B[?25h\x1B[0m");
|
|
1320
1626
|
process.on("exit", restoreCursor);
|
|
1321
1627
|
try {
|
|
@@ -1327,7 +1633,7 @@ async function playInstallBanner() {
|
|
|
1327
1633
|
put(cvs, nx(CENTER), ny(CENTER), seedCh[f % 4], seedCo[f % 4]);
|
|
1328
1634
|
write(flush(cvs));
|
|
1329
1635
|
write(`
|
|
1330
|
-
${CO.DIM}Seed detected
|
|
1636
|
+
${CO.DIM}Seed detected...\x1B[K${CO.VOID}
|
|
1331
1637
|
`);
|
|
1332
1638
|
await sleep(60);
|
|
1333
1639
|
}
|
|
@@ -1374,7 +1680,7 @@ async function playInstallBanner() {
|
|
|
1374
1680
|
let active = 0;
|
|
1375
1681
|
for (let i = 0; i < nc; i++) if (isGrown[i]) active++;
|
|
1376
1682
|
write(`
|
|
1377
|
-
\x1B[0;35mNodes \x1B[1;36m${active}\x1B[0;35m/${nc}\x1B[0m \x1B[0;90mGrowing neural tendrils...\x1B[0m
|
|
1683
|
+
\x1B[0;35mNodes \x1B[1;36m${active}\x1B[0;35m/${nc}\x1B[0m \x1B[0;90mGrowing neural tendrils...\x1B[K\x1B[0m
|
|
1378
1684
|
`);
|
|
1379
1685
|
await sleep(40);
|
|
1380
1686
|
}
|
|
@@ -1397,7 +1703,7 @@ async function playInstallBanner() {
|
|
|
1397
1703
|
renderParticles(cvs, ps, f);
|
|
1398
1704
|
write(flush(cvs));
|
|
1399
1705
|
write(`
|
|
1400
|
-
${CO.SETTLED}Crystallizing pathways
|
|
1706
|
+
${CO.SETTLED}Crystallizing pathways...\x1B[K${CO.VOID}
|
|
1401
1707
|
`);
|
|
1402
1708
|
await sleep(60);
|
|
1403
1709
|
}
|
|
@@ -1476,6 +1782,10 @@ async function playInstallBanner() {
|
|
|
1476
1782
|
process.removeListener("exit", restoreCursor);
|
|
1477
1783
|
restoreCursor();
|
|
1478
1784
|
write("\n");
|
|
1785
|
+
if (audio) {
|
|
1786
|
+
await sleep(1800);
|
|
1787
|
+
audio.stop();
|
|
1788
|
+
}
|
|
1479
1789
|
}
|
|
1480
1790
|
}
|
|
1481
1791
|
function checkBeadsAvailable() {
|
|
@@ -4532,7 +4842,7 @@ npx ca loop --force # Overwrite existing script
|
|
|
4532
4842
|
## Health, audit, and verification commands
|
|
4533
4843
|
|
|
4534
4844
|
\`\`\`bash
|
|
4535
|
-
npx ca
|
|
4845
|
+
npx ca about # Show version, animation, and recent changelog
|
|
4536
4846
|
npx ca doctor # Check external dependencies and project health
|
|
4537
4847
|
npx ca audit # Run pattern, rule, and lesson quality checks
|
|
4538
4848
|
npx ca rules check # Check codebase against .claude/rules.json
|
|
@@ -7365,88 +7675,51 @@ function registerVerifyGatesCommand(program2) {
|
|
|
7365
7675
|
}
|
|
7366
7676
|
|
|
7367
7677
|
// src/changelog-data.ts
|
|
7368
|
-
var CHANGELOG_RECENT = `## [1.3.
|
|
7369
|
-
|
|
7370
|
-
### Added
|
|
7371
|
-
|
|
7372
|
-
- **\`ca version-show\` command**: Displays version with terminal animation (tendril growth) and recent changelog entries. Non-TTY environments get plain text output. Changelog is embedded at build time from CHANGELOG.md.
|
|
7373
|
-
- **3 new doctor checks**: Beads initialized (\`.beads/\` dir), beads healthy (\`bd doctor\`), codebase scope (user-scope detection)
|
|
7374
|
-
- **Beads + scope status in init/setup output**: Full beads health display (CLI available, initialized, healthy) and scope status shown after \`ca init\`, \`ca setup\`, and \`ca setup --update\`
|
|
7375
|
-
- **Banner on \`--update\`**: Terminal art animation now plays during \`ca setup --update\` and \`ca init --update\` (same TTY/quiet guards as fresh install)
|
|
7678
|
+
var CHANGELOG_RECENT = `## [1.3.3] - 2026-02-21
|
|
7376
7679
|
|
|
7377
7680
|
### Changed
|
|
7378
7681
|
|
|
7379
|
-
- **
|
|
7380
|
-
- **Test-cleaner Phase 3 strengthened**: Adversarial review phase now mandates iteration loop until both reviewers give unconditional approval. Heading, emphasis, and quality criteria updated.
|
|
7381
|
-
- **Update hint on upgrade**: When \`ca init\` or \`ca setup\` detects an existing install, displays tip to run with \`--update\` to regenerate managed files
|
|
7382
|
-
- **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)
|
|
7383
|
-
- **Doctor doc check**: Now checks for \`docs/compound/README.md\` instead of \`HOW_TO_COMPOUND.md\`
|
|
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.
|
|
7384
7683
|
|
|
7385
|
-
## [1.3.
|
|
7684
|
+
## [1.3.2] - 2026-02-21
|
|
7386
7685
|
|
|
7387
7686
|
### Added
|
|
7388
7687
|
|
|
7389
|
-
- **
|
|
7390
|
-
|
|
7391
|
-
- **User-scope detection** (\`scope-check.ts\`): Warns when installing at home directory level where lessons are shared across projects
|
|
7392
|
-
- **.gitignore injection** (\`gitignore.ts\`): Ensures \`node_modules/\` and \`.claude/.cache/\` patterns exist in \`.gitignore\`
|
|
7393
|
-
- **Upgrade detection** (\`upgrade.ts\`): Detects existing installs and runs migration pipeline (deprecated command removal, header stripping, doc version update)
|
|
7394
|
-
- **Upgrade engine**: Automated migration from v1.2.x to v1.3.0:
|
|
7395
|
-
- Removes 5 deprecated CLI wrapper commands (\`search.md\`, \`list.md\`, \`show.md\`, \`stats.md\`, \`wrong.md\`)
|
|
7396
|
-
- Strips legacy \`<!-- generated by compound-agent -->\` headers from installed files
|
|
7397
|
-
- Updates \`HOW_TO_COMPOUND.md\` version during upgrade
|
|
7398
|
-
- **\`/compound:research\` skill**: PhD-depth research producing structured survey documents following \`TEMPLATE_FOR_RESEARCH.md\` format
|
|
7399
|
-
- **\`/compound:test-clean\` skill**: 5-phase test suite optimization with adversarial review (audit, design, implement, verify, report)
|
|
7400
|
-
- **Documentation template**: \`HOW_TO_COMPOUND.md\` deployed to \`docs/compound/\` during setup with version and date placeholders
|
|
7401
|
-
- **Test scripts**: \`test:segment\` (run tests for specific module), \`test:random\` (seeded random subset), \`test:critical\` (*.critical.test.ts convention)
|
|
7402
|
-
- **3 new doctor checks**: Beads CLI availability, \`.gitignore\` health, usage documentation presence
|
|
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.
|
|
7689
|
+
- **Test coverage**: 19 new tests for \`ca about\` command, changelog extraction/escaping, and \`--update\` doc migration path
|
|
7403
7690
|
|
|
7404
7691
|
### Fixed
|
|
7405
7692
|
|
|
7406
|
-
- **\`setup --update
|
|
7407
|
-
-
|
|
7408
|
-
- **
|
|
7409
|
-
- **Malformed settings.json no longer silently clobbered**: On parse error, \`configureClaudeSettings\` warns and skips instead of overwriting with empty config
|
|
7693
|
+
- **\`setup --update\` doc migration**: \`--update\` now installs the 5 split docs before removing legacy \`HOW_TO_COMPOUND.md\`, preventing empty \`docs/compound/\`
|
|
7694
|
+
- **Fresh checkout type-check**: \`src/changelog-data.ts\` tracked in git so \`tsc --noEmit\` passes without a prior build
|
|
7695
|
+
- **Trailing status text**: Banner animation no longer leaves "al tendrils..." remnant from previous phase
|
|
7410
7696
|
|
|
7411
7697
|
### Changed
|
|
7412
7698
|
|
|
7413
|
-
- **\`
|
|
7414
|
-
- **
|
|
7415
|
-
- **
|
|
7416
|
-
- **Beads check portable**: Uses POSIX \`command -v\` instead of non-portable \`which\`
|
|
7417
|
-
- **Template expansion**: Brainstorm and plan skills now cross-reference researcher skill; 9 total skills, 11 total commands
|
|
7418
|
-
- **Code organization**: Extracted display utilities to \`display-utils.ts\`, uninstall logic to \`uninstall.ts\`
|
|
7699
|
+
- **\`ca about\` command**: Renamed from \`ca version-show\` for brevity
|
|
7700
|
+
- **Changelog extraction**: Core parsing/escaping logic extracted to \`scripts/changelog-utils.ts\` (shared between prebuild script and tests)
|
|
7701
|
+
- **Narrowed \`.gitignore\`**: Setup-generated patterns scoped to \`compound/\` subdirectories to avoid hiding tracked TDD agent definitions
|
|
7419
7702
|
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
- **5 deprecated CLI wrapper commands**: \`search.md\`, \`list.md\`, \`show.md\`, \`stats.md\`, \`wrong.md\` (redundant wrappers around \`npx ca <cmd>\`)
|
|
7423
|
-
- **\`GENERATED_MARKER\` on new installs**: New installs use path-based detection; marker retained only for backward-compatible \`--update\` detection
|
|
7424
|
-
|
|
7425
|
-
## [1.2.11] - 2026-02-19
|
|
7703
|
+
## [1.3.1] - 2026-02-21
|
|
7426
7704
|
|
|
7427
7705
|
### Added
|
|
7428
7706
|
|
|
7429
|
-
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
- \`ca worktree list\` \u2014 Show active worktrees with epic and merge task status
|
|
7434
|
-
- \`ca worktree cleanup <epic-id>\` \u2014 Remove worktree, branch, and close Merge task (--force for dirty worktrees)
|
|
7435
|
-
- **\`/compound:set-worktree\` slash command**: Set up a worktree before running \`/compound:lfg\` for isolated epic execution
|
|
7436
|
-
- **Conditional Merge gate in \`verify-gates\`**: Worktree epics require the Merge task to be closed before epic closure. Non-worktree epics unaffected.
|
|
7437
|
-
- **Plan skill wire-deps step**: Plan phase now calls \`ca worktree wire-deps\` to connect merge dependencies when a worktree is active.
|
|
7707
|
+
- **\`ca about\` command**: Displays version with terminal animation (tendril growth) and recent changelog entries. Non-TTY environments get plain text output. Changelog is embedded at build time from CHANGELOG.md.
|
|
7708
|
+
- **3 new doctor checks**: Beads initialized (\`.beads/\` dir), beads healthy (\`bd doctor\`), codebase scope (user-scope detection)
|
|
7709
|
+
- **Beads + scope status in init/setup output**: Full beads health display (CLI available, initialized, healthy) and scope status shown after \`ca init\`, \`ca setup\`, and \`ca setup --update\`
|
|
7710
|
+
- **Banner on \`--update\`**: Terminal art animation now plays during \`ca setup --update\` and \`ca init --update\` (same TTY/quiet guards as fresh install)
|
|
7438
7711
|
|
|
7439
7712
|
### Changed
|
|
7440
7713
|
|
|
7441
|
-
- **
|
|
7442
|
-
- **
|
|
7443
|
-
- **
|
|
7444
|
-
- **
|
|
7445
|
-
- **
|
|
7714
|
+
- **Split documentation**: \`HOW_TO_COMPOUND.md\` replaced by 5 focused documents in \`docs/compound/\`: \`README.md\`, \`WORKFLOW.md\`, \`CLI_REFERENCE.md\`, \`SKILLS.md\`, \`INTEGRATION.md\`
|
|
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.
|
|
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
|
|
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)
|
|
7718
|
+
- **Doctor doc check**: Now checks for \`docs/compound/README.md\` instead of \`HOW_TO_COMPOUND.md\``;
|
|
7446
7719
|
|
|
7447
|
-
// src/commands/
|
|
7448
|
-
function
|
|
7449
|
-
program2.command("
|
|
7720
|
+
// src/commands/about.ts
|
|
7721
|
+
function registerAboutCommand(program2) {
|
|
7722
|
+
program2.command("about").description("Show version, animation, and recent changelog").action(async () => {
|
|
7450
7723
|
if (process.stdout.isTTY) {
|
|
7451
7724
|
await playInstallBanner();
|
|
7452
7725
|
} else {
|
|
@@ -8564,7 +8837,7 @@ function registerManagementCommands(program2) {
|
|
|
8564
8837
|
registerRulesCommands(program2);
|
|
8565
8838
|
registerTestSummaryCommand(program2);
|
|
8566
8839
|
registerVerifyGatesCommand(program2);
|
|
8567
|
-
|
|
8840
|
+
registerAboutCommand(program2);
|
|
8568
8841
|
registerWorktreeCommands(program2);
|
|
8569
8842
|
}
|
|
8570
8843
|
|