compound-agent 1.3.1 → 1.3.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 +20 -1
- package/README.md +1 -1
- package/dist/cli.js +199 -35
- 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,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.3.2] - 2026-02-21
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Banner audio**: Pure TypeScript WAV synthesis plays a rising pentatonic melody during the tendril animation. Cross-platform: `afplay` (macOS), `aplay` (Linux), PowerShell (Windows). Silently skips if player unavailable. Zero dependencies.
|
|
17
|
+
- **Test coverage**: 19 new tests for `ca about` command, changelog extraction/escaping, and `--update` doc migration path
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`setup --update` doc migration**: `--update` now installs the 5 split docs before removing legacy `HOW_TO_COMPOUND.md`, preventing empty `docs/compound/`
|
|
22
|
+
- **Fresh checkout type-check**: `src/changelog-data.ts` tracked in git so `tsc --noEmit` passes without a prior build
|
|
23
|
+
- **Trailing status text**: Banner animation no longer leaves "al tendrils..." remnant from previous phase
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`ca about` command**: Renamed from `ca version-show` for brevity
|
|
28
|
+
- **Changelog extraction**: Core parsing/escaping logic extracted to `scripts/changelog-utils.ts` (shared between prebuild script and tests)
|
|
29
|
+
- **Narrowed `.gitignore`**: Setup-generated patterns scoped to `compound/` subdirectories to avoid hiding tracked TDD agent definitions
|
|
30
|
+
|
|
12
31
|
## [1.3.1] - 2026-02-21
|
|
13
32
|
|
|
14
33
|
### Added
|
|
15
34
|
|
|
16
|
-
- **`ca
|
|
35
|
+
- **`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
36
|
- **3 new doctor checks**: Beads initialized (`.beads/` dir), beads healthy (`bd doctor`), codebase scope (user-scope detection)
|
|
18
37
|
- **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
38
|
- **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,171 @@ 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 SAMPLE_RATE = 22050;
|
|
1139
|
+
var BITS = 16;
|
|
1140
|
+
var MAX_AMP = 24576;
|
|
1141
|
+
var NOTE = {
|
|
1142
|
+
C3: 131,
|
|
1143
|
+
E3: 165,
|
|
1144
|
+
G3: 196,
|
|
1145
|
+
C4: 262,
|
|
1146
|
+
E4: 330,
|
|
1147
|
+
G4: 392,
|
|
1148
|
+
C5: 523,
|
|
1149
|
+
E5: 659,
|
|
1150
|
+
G5: 784,
|
|
1151
|
+
C6: 1047
|
|
1152
|
+
};
|
|
1153
|
+
function envelope(i, total, attack, release) {
|
|
1154
|
+
if (i < attack) return i / attack;
|
|
1155
|
+
if (i > total - release) return (total - i) / release;
|
|
1156
|
+
return 1;
|
|
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);
|
|
1162
|
+
const out2 = [];
|
|
1163
|
+
for (let i = 0; i < samples; i++) {
|
|
1164
|
+
const env = envelope(i, samples, attack, release);
|
|
1165
|
+
out2.push(Math.sin(2 * Math.PI * freq * i / SAMPLE_RATE) * env * amp);
|
|
1166
|
+
}
|
|
1167
|
+
return out2;
|
|
1168
|
+
}
|
|
1169
|
+
function chord(freqs, durationMs, amp = 1) {
|
|
1170
|
+
const tones = freqs.map((f) => tone(f, durationMs, 1));
|
|
1171
|
+
const len = tones[0].length;
|
|
1172
|
+
const out2 = [];
|
|
1173
|
+
const scale = amp / freqs.length;
|
|
1174
|
+
for (let i = 0; i < len; i++) {
|
|
1175
|
+
let sum = 0;
|
|
1176
|
+
for (const t of tones) sum += t[i];
|
|
1177
|
+
out2.push(sum * scale);
|
|
1178
|
+
}
|
|
1179
|
+
return out2;
|
|
1180
|
+
}
|
|
1181
|
+
function silence(durationMs) {
|
|
1182
|
+
return new Array(Math.floor(SAMPLE_RATE * durationMs / 1e3)).fill(0);
|
|
1183
|
+
}
|
|
1184
|
+
function composeMelody() {
|
|
1185
|
+
const samples = [];
|
|
1186
|
+
samples.push(...tone(NOTE.C3, 250, 0.3));
|
|
1187
|
+
samples.push(...silence(150));
|
|
1188
|
+
samples.push(...tone(NOTE.C3, 250, 0.35));
|
|
1189
|
+
samples.push(...silence(100));
|
|
1190
|
+
samples.push(...tone(NOTE.E3, 200, 0.3));
|
|
1191
|
+
samples.push(...silence(80));
|
|
1192
|
+
samples.push(...tone(NOTE.C4, 140, 0.45));
|
|
1193
|
+
samples.push(...silence(30));
|
|
1194
|
+
samples.push(...tone(NOTE.E4, 140, 0.5));
|
|
1195
|
+
samples.push(...silence(30));
|
|
1196
|
+
samples.push(...tone(NOTE.G4, 140, 0.5));
|
|
1197
|
+
samples.push(...silence(30));
|
|
1198
|
+
samples.push(...tone(NOTE.C5, 160, 0.55));
|
|
1199
|
+
samples.push(...silence(30));
|
|
1200
|
+
samples.push(...tone(NOTE.E5, 160, 0.55));
|
|
1201
|
+
samples.push(...silence(30));
|
|
1202
|
+
samples.push(...tone(NOTE.G5, 180, 0.6));
|
|
1203
|
+
samples.push(...silence(60));
|
|
1204
|
+
samples.push(...tone(NOTE.C4, 100, 0.4));
|
|
1205
|
+
samples.push(...tone(NOTE.E4, 100, 0.45));
|
|
1206
|
+
samples.push(...tone(NOTE.G4, 100, 0.45));
|
|
1207
|
+
samples.push(...tone(NOTE.C5, 120, 0.5));
|
|
1208
|
+
samples.push(...tone(NOTE.E5, 120, 0.55));
|
|
1209
|
+
samples.push(...tone(NOTE.G5, 140, 0.6));
|
|
1210
|
+
samples.push(...silence(60));
|
|
1211
|
+
samples.push(...tone(NOTE.C6, 80, 0.3));
|
|
1212
|
+
samples.push(...silence(40));
|
|
1213
|
+
samples.push(...tone(NOTE.G5, 80, 0.35));
|
|
1214
|
+
samples.push(...silence(40));
|
|
1215
|
+
samples.push(...tone(NOTE.C6, 80, 0.3));
|
|
1216
|
+
samples.push(...silence(40));
|
|
1217
|
+
samples.push(...tone(NOTE.E5, 100, 0.35));
|
|
1218
|
+
samples.push(...silence(60));
|
|
1219
|
+
samples.push(...chord([NOTE.C4, NOTE.E4, NOTE.G4], 600, 0.7));
|
|
1220
|
+
samples.push(...chord([NOTE.C4, NOTE.E4, NOTE.G4, NOTE.C5], 500, 0.6));
|
|
1221
|
+
samples.push(...chord([NOTE.C3, NOTE.G3, NOTE.C4], 1200, 0.35));
|
|
1222
|
+
return samples;
|
|
1223
|
+
}
|
|
1224
|
+
function encodeWav(samples) {
|
|
1225
|
+
const dataSize = samples.length * (BITS / 8);
|
|
1226
|
+
const fileSize = 44 + dataSize;
|
|
1227
|
+
const buf = Buffer.alloc(fileSize);
|
|
1228
|
+
buf.write("RIFF", 0);
|
|
1229
|
+
buf.writeUInt32LE(fileSize - 8, 4);
|
|
1230
|
+
buf.write("WAVE", 8);
|
|
1231
|
+
buf.write("fmt ", 12);
|
|
1232
|
+
buf.writeUInt32LE(16, 16);
|
|
1233
|
+
buf.writeUInt16LE(1, 20);
|
|
1234
|
+
buf.writeUInt16LE(1, 22);
|
|
1235
|
+
buf.writeUInt32LE(SAMPLE_RATE, 24);
|
|
1236
|
+
buf.writeUInt32LE(SAMPLE_RATE * BITS / 8, 28);
|
|
1237
|
+
buf.writeUInt16LE(BITS / 8, 32);
|
|
1238
|
+
buf.writeUInt16LE(BITS, 34);
|
|
1239
|
+
buf.write("data", 36);
|
|
1240
|
+
buf.writeUInt32LE(dataSize, 40);
|
|
1241
|
+
let offset = 44;
|
|
1242
|
+
for (const s of samples) {
|
|
1243
|
+
const clamped = Math.max(-1, Math.min(1, s));
|
|
1244
|
+
buf.writeInt16LE(Math.round(clamped * MAX_AMP), offset);
|
|
1245
|
+
offset += 2;
|
|
1246
|
+
}
|
|
1247
|
+
return buf;
|
|
1248
|
+
}
|
|
1249
|
+
function spawnPlayer(filePath) {
|
|
1250
|
+
try {
|
|
1251
|
+
switch (process.platform) {
|
|
1252
|
+
case "darwin":
|
|
1253
|
+
return spawn("afplay", [filePath], { stdio: "ignore", detached: true });
|
|
1254
|
+
case "linux":
|
|
1255
|
+
return spawn("aplay", ["-q", filePath], { stdio: "ignore", detached: true });
|
|
1256
|
+
case "win32":
|
|
1257
|
+
return spawn("powershell", [
|
|
1258
|
+
"-c",
|
|
1259
|
+
`(New-Object Media.SoundPlayer '${filePath}').PlaySync()`
|
|
1260
|
+
], { stdio: "ignore", detached: true });
|
|
1261
|
+
default:
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
} catch {
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
function playBannerAudio() {
|
|
1269
|
+
try {
|
|
1270
|
+
const wav = encodeWav(composeMelody());
|
|
1271
|
+
const tmpPath = join(tmpdir(), `ca-banner-${process.pid}.wav`);
|
|
1272
|
+
writeFileSync(tmpPath, wav);
|
|
1273
|
+
const proc = spawnPlayer(tmpPath);
|
|
1274
|
+
if (!proc) {
|
|
1275
|
+
try {
|
|
1276
|
+
unlinkSync(tmpPath);
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
proc.unref();
|
|
1282
|
+
const cleanup2 = () => {
|
|
1283
|
+
try {
|
|
1284
|
+
proc.kill();
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
1287
|
+
try {
|
|
1288
|
+
unlinkSync(tmpPath);
|
|
1289
|
+
} catch {
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
proc.on("exit", () => {
|
|
1293
|
+
try {
|
|
1294
|
+
unlinkSync(tmpPath);
|
|
1295
|
+
} catch {
|
|
1296
|
+
}
|
|
1297
|
+
});
|
|
1298
|
+
return { stop: cleanup2 };
|
|
1299
|
+
} catch {
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1138
1303
|
|
|
1139
1304
|
// src/setup/banner.ts
|
|
1140
1305
|
var W = 62;
|
|
@@ -1316,6 +1481,7 @@ async function playInstallBanner() {
|
|
|
1316
1481
|
const ps = [];
|
|
1317
1482
|
const write = (s) => process.stdout.write(s);
|
|
1318
1483
|
write("\x1B[?25l\x1B[2J\x1B[H");
|
|
1484
|
+
const audio = playBannerAudio();
|
|
1319
1485
|
const restoreCursor = () => process.stdout.write("\x1B[?25h\x1B[0m");
|
|
1320
1486
|
process.on("exit", restoreCursor);
|
|
1321
1487
|
try {
|
|
@@ -1327,7 +1493,7 @@ async function playInstallBanner() {
|
|
|
1327
1493
|
put(cvs, nx(CENTER), ny(CENTER), seedCh[f % 4], seedCo[f % 4]);
|
|
1328
1494
|
write(flush(cvs));
|
|
1329
1495
|
write(`
|
|
1330
|
-
${CO.DIM}Seed detected
|
|
1496
|
+
${CO.DIM}Seed detected...\x1B[K${CO.VOID}
|
|
1331
1497
|
`);
|
|
1332
1498
|
await sleep(60);
|
|
1333
1499
|
}
|
|
@@ -1374,7 +1540,7 @@ async function playInstallBanner() {
|
|
|
1374
1540
|
let active = 0;
|
|
1375
1541
|
for (let i = 0; i < nc; i++) if (isGrown[i]) active++;
|
|
1376
1542
|
write(`
|
|
1377
|
-
\x1B[0;35mNodes \x1B[1;36m${active}\x1B[0;35m/${nc}\x1B[0m \x1B[0;90mGrowing neural tendrils...\x1B[0m
|
|
1543
|
+
\x1B[0;35mNodes \x1B[1;36m${active}\x1B[0;35m/${nc}\x1B[0m \x1B[0;90mGrowing neural tendrils...\x1B[K\x1B[0m
|
|
1378
1544
|
`);
|
|
1379
1545
|
await sleep(40);
|
|
1380
1546
|
}
|
|
@@ -1397,7 +1563,7 @@ async function playInstallBanner() {
|
|
|
1397
1563
|
renderParticles(cvs, ps, f);
|
|
1398
1564
|
write(flush(cvs));
|
|
1399
1565
|
write(`
|
|
1400
|
-
${CO.SETTLED}Crystallizing pathways
|
|
1566
|
+
${CO.SETTLED}Crystallizing pathways...\x1B[K${CO.VOID}
|
|
1401
1567
|
`);
|
|
1402
1568
|
await sleep(60);
|
|
1403
1569
|
}
|
|
@@ -1473,6 +1639,7 @@ async function playInstallBanner() {
|
|
|
1473
1639
|
await sleep(120);
|
|
1474
1640
|
}
|
|
1475
1641
|
} finally {
|
|
1642
|
+
audio?.stop();
|
|
1476
1643
|
process.removeListener("exit", restoreCursor);
|
|
1477
1644
|
restoreCursor();
|
|
1478
1645
|
write("\n");
|
|
@@ -4532,7 +4699,7 @@ npx ca loop --force # Overwrite existing script
|
|
|
4532
4699
|
## Health, audit, and verification commands
|
|
4533
4700
|
|
|
4534
4701
|
\`\`\`bash
|
|
4535
|
-
npx ca
|
|
4702
|
+
npx ca about # Show version, animation, and recent changelog
|
|
4536
4703
|
npx ca doctor # Check external dependencies and project health
|
|
4537
4704
|
npx ca audit # Run pattern, rule, and lesson quality checks
|
|
4538
4705
|
npx ca rules check # Check codebase against .claude/rules.json
|
|
@@ -7365,11 +7532,30 @@ function registerVerifyGatesCommand(program2) {
|
|
|
7365
7532
|
}
|
|
7366
7533
|
|
|
7367
7534
|
// src/changelog-data.ts
|
|
7368
|
-
var CHANGELOG_RECENT = `## [1.3.
|
|
7535
|
+
var CHANGELOG_RECENT = `## [1.3.2] - 2026-02-21
|
|
7369
7536
|
|
|
7370
7537
|
### Added
|
|
7371
7538
|
|
|
7372
|
-
-
|
|
7539
|
+
- **Banner audio**: Pure TypeScript WAV synthesis plays a rising pentatonic melody during the tendril animation. Cross-platform: \`afplay\` (macOS), \`aplay\` (Linux), PowerShell (Windows). Silently skips if player unavailable. Zero dependencies.
|
|
7540
|
+
- **Test coverage**: 19 new tests for \`ca about\` command, changelog extraction/escaping, and \`--update\` doc migration path
|
|
7541
|
+
|
|
7542
|
+
### Fixed
|
|
7543
|
+
|
|
7544
|
+
- **\`setup --update\` doc migration**: \`--update\` now installs the 5 split docs before removing legacy \`HOW_TO_COMPOUND.md\`, preventing empty \`docs/compound/\`
|
|
7545
|
+
- **Fresh checkout type-check**: \`src/changelog-data.ts\` tracked in git so \`tsc --noEmit\` passes without a prior build
|
|
7546
|
+
- **Trailing status text**: Banner animation no longer leaves "al tendrils..." remnant from previous phase
|
|
7547
|
+
|
|
7548
|
+
### Changed
|
|
7549
|
+
|
|
7550
|
+
- **\`ca about\` command**: Renamed from \`ca version-show\` for brevity
|
|
7551
|
+
- **Changelog extraction**: Core parsing/escaping logic extracted to \`scripts/changelog-utils.ts\` (shared between prebuild script and tests)
|
|
7552
|
+
- **Narrowed \`.gitignore\`**: Setup-generated patterns scoped to \`compound/\` subdirectories to avoid hiding tracked TDD agent definitions
|
|
7553
|
+
|
|
7554
|
+
## [1.3.1] - 2026-02-21
|
|
7555
|
+
|
|
7556
|
+
### Added
|
|
7557
|
+
|
|
7558
|
+
- **\`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.
|
|
7373
7559
|
- **3 new doctor checks**: Beads initialized (\`.beads/\` dir), beads healthy (\`bd doctor\`), codebase scope (user-scope detection)
|
|
7374
7560
|
- **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
7561
|
- **Banner on \`--update\`**: Terminal art animation now plays during \`ca setup --update\` and \`ca init --update\` (same TTY/quiet guards as fresh install)
|
|
@@ -7420,33 +7606,11 @@ var CHANGELOG_RECENT = `## [1.3.1] - 2026-02-21
|
|
|
7420
7606
|
### Removed
|
|
7421
7607
|
|
|
7422
7608
|
- **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
|
|
7426
|
-
|
|
7427
|
-
### Added
|
|
7428
|
-
|
|
7429
|
-
- **Git worktree integration** (\`ca worktree\`): Isolate epic work in separate git worktrees for parallel execution. Five subcommands:
|
|
7430
|
-
- \`ca worktree create <epic-id>\` \u2014 Create worktree, install deps, copy lessons, create Merge beads task
|
|
7431
|
-
- \`ca worktree wire-deps <epic-id>\` \u2014 Connect Review/Compound tasks as merge blockers (graceful no-op without worktree)
|
|
7432
|
-
- \`ca worktree merge <epic-id>\` \u2014 Two-phase merge: resolve conflicts in worktree, then land clean on main
|
|
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.
|
|
7438
|
-
|
|
7439
|
-
### Changed
|
|
7440
|
-
|
|
7441
|
-
- **Worktree merge safety hardening**: Added branch verification (asserts main repo is on \`main\`), worktree existence guard, structured error messages with worktree paths for conflict resolution and test failures
|
|
7442
|
-
- **JSONL reconciliation**: Switched from ID-based to line-based deduplication to preserve last-write-wins semantics for same-ID updates and deletes
|
|
7443
|
-
- **Worktree cleanup safety**: Branch deletion uses \`-d\` (safe) by default; \`-D\` (force) only with \`--force\` flag
|
|
7444
|
-
- **Shared beads utilities**: Extracted \`validateEpicId\`, \`parseBdShowDeps\`, and \`shortId\` to \`cli-utils.ts\`, eliminating duplication between \`worktree.ts\` and \`verify-gates.ts\`
|
|
7445
|
-
- **Sync API**: All worktree functions are now synchronous (removed misleading \`async\` wrapper around purely synchronous \`execFileSync\` calls)`;
|
|
7609
|
+
- **\`GENERATED_MARKER\` on new installs**: New installs use path-based detection; marker retained only for backward-compatible \`--update\` detection`;
|
|
7446
7610
|
|
|
7447
|
-
// src/commands/
|
|
7448
|
-
function
|
|
7449
|
-
program2.command("
|
|
7611
|
+
// src/commands/about.ts
|
|
7612
|
+
function registerAboutCommand(program2) {
|
|
7613
|
+
program2.command("about").description("Show version, animation, and recent changelog").action(async () => {
|
|
7450
7614
|
if (process.stdout.isTTY) {
|
|
7451
7615
|
await playInstallBanner();
|
|
7452
7616
|
} else {
|
|
@@ -8564,7 +8728,7 @@ function registerManagementCommands(program2) {
|
|
|
8564
8728
|
registerRulesCommands(program2);
|
|
8565
8729
|
registerTestSummaryCommand(program2);
|
|
8566
8730
|
registerVerifyGatesCommand(program2);
|
|
8567
|
-
|
|
8731
|
+
registerAboutCommand(program2);
|
|
8568
8732
|
registerWorktreeCommands(program2);
|
|
8569
8733
|
}
|
|
8570
8734
|
|