Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,15 @@ model via `SEC_MERGER_PROXY_MODEL` (default `claude-sonnet-4-6`) and an optional
confidence floor via `SEC_MERGER_PROXY_CONFIDENCE_FLOOR` (falls back to the shared
`SEC_S1_CONFIDENCE_FLOOR` when unset).

A proxy ingested before its issuer's `spac` row exists (e.g. the S-1 lands later)
hits the known-SPAC gate and no-ops — recording a successful run, so the normal
unprocessed-run sweep never revisits it. `sec spac backfill-merger-proxies`
recovers these: it re-processes known-SPAC merger proxies that still lack a
`spac_merger_extraction` row (mirroring `backfill-redemptions`).

```bash
sec fetch form <cik> DEFM14A # fetch + extract a merger proxy
sec spac backfill-merger-proxies # recover proxies gated before their spac row existed
sec extractor dead-letters merger-proxy # version-fixable extraction failures
sec extractor retry-dead-letters merger-proxy
```
Expand Down
54 changes: 11 additions & 43 deletions src/cli/GlobalOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ function createProgram(): Command {

describe("GlobalOptions", () => {
describe("applyGlobalOptions", () => {
it("adds all flags to help text", () => {
it("adds --dry-run to help text", () => {
const program = createProgram();
const help = program.helpInformation();
expect(help).toContain("--json");
expect(help).toContain("--verbose");
expect(help).toContain("--dry-run");
expect(help).toContain("--no-color");
});

it("does not advertise the removed (never-consumed) flags", () => {
const help = createProgram().helpInformation();
expect(help).not.toContain("--json");
expect(help).not.toContain("--verbose");
expect(help).not.toContain("--no-color");
});

it("returns the program for chaining", () => {
Expand All @@ -27,53 +31,17 @@ describe("GlobalOptions", () => {
});
});

describe("parseGlobalOptions defaults", () => {
it("returns correct defaults when no flags are set", () => {
describe("parseGlobalOptions", () => {
it("defaults dryRun to false", () => {
const program = createProgram();
program.parse([], { from: "user" });
const opts = parseGlobalOptions(program);
expect(opts.json).toBe(false);
expect(opts.verbose).toBe(false);
expect(opts.dryRun).toBe(false);
expect(opts.color).toBe(true);
});
});

describe("parseGlobalOptions with flags", () => {
it("parses --json", () => {
const program = createProgram();
program.parse(["--json"], { from: "user" });
expect(parseGlobalOptions(program).json).toBe(true);
});

it("parses --verbose", () => {
const program = createProgram();
program.parse(["--verbose"], { from: "user" });
expect(parseGlobalOptions(program).verbose).toBe(true);
expect(parseGlobalOptions(program).dryRun).toBe(false);
});

it("parses --dry-run", () => {
const program = createProgram();
program.parse(["--dry-run"], { from: "user" });
expect(parseGlobalOptions(program).dryRun).toBe(true);
});

it("parses --no-color", () => {
const program = createProgram();
program.parse(["--no-color"], { from: "user" });
expect(parseGlobalOptions(program).color).toBe(false);
});

it("parses all flags together", () => {
const program = createProgram();
program.parse(["--json", "--verbose", "--dry-run", "--no-color"], {
from: "user",
});
const opts = parseGlobalOptions(program);
expect(opts.json).toBe(true);
expect(opts.verbose).toBe(true);
expect(opts.dryRun).toBe(true);
expect(opts.color).toBe(false);
});
});
});
16 changes: 5 additions & 11 deletions src/cli/GlobalOptions.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import type { Command } from "commander";

export interface GlobalOptions {
readonly json: boolean;
readonly verbose: boolean;
readonly dryRun: boolean;
readonly color: boolean;
}

// Only `--dry-run` is wired (it gates writes via SEC_DRY_RUN). Previous
// `--json` / `--verbose` / `--no-color` flags were parsed but never consumed —
// read commands carry their own `--format`, and output is plain text — so they
// were removed rather than advertised in --help while doing nothing.
export function applyGlobalOptions(program: Command): Command {
return program
.option("--json", "Force JSON output", false)
.option("--verbose", "Show detailed logs", false)
.option("--dry-run", "Show what would happen without changes", false)
.option("--no-color", "Disable colored output");
return program.option("--dry-run", "Show what would happen without changes", false);
}

export function parseGlobalOptions(cmd: Command): GlobalOptions {
const opts = cmd.opts();
return {
json: opts.json ?? false,
verbose: opts.verbose ?? false,
dryRun: opts.dryRun ?? false,
color: opts.color ?? true,
};
}

Expand Down
3 changes: 0 additions & 3 deletions src/cli/cli.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ describe("CLI v2 integration", () => {

it("should show global options", async () => {
const output = await runCli("--help");
expect(output).toContain("--json");
expect(output).toContain("--verbose");
expect(output).toContain("--dry-run");
expect(output).toContain("--no-color");
});

it("should show version 2.0.0", async () => {
Expand Down
24 changes: 16 additions & 8 deletions src/cli/groups/canonical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,13 @@ export function addCanonicalCommands(program: Command): void {
intoId = await resolveCanonicalPersonRef(into, canonRepo);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
if (fromId === intoId) {
console.error("error: cannot alias an id to itself");
process.exit(1);
process.exitCode = 1;
return;
}
const aliasRepo = new CanonicalPersonAliasRepo();
try {
Expand All @@ -112,7 +114,8 @@ export function addCanonicalCommands(program: Command): void {
console.log(`aliased ${row.alias_canonical_id} → ${row.target_canonical_id}`);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
});

Expand All @@ -125,7 +128,8 @@ export function addCanonicalCommands(program: Command): void {
fromId = await resolveCanonicalPersonRef(from, canonRepo);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
const aliasRepo = new CanonicalPersonAliasRepo();
await aliasRepo.remove(fromId);
Expand Down Expand Up @@ -167,11 +171,13 @@ export function addCanonicalCommands(program: Command): void {
intoId = await resolveCanonicalCompanyRef(into, canonRepo);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
if (fromId === intoId) {
console.error("error: cannot alias an id to itself");
process.exit(1);
process.exitCode = 1;
return;
}
const aliasRepo = new CanonicalCompanyAliasRepo();
try {
Expand All @@ -184,7 +190,8 @@ export function addCanonicalCommands(program: Command): void {
console.log(`aliased ${row.alias_canonical_id} → ${row.target_canonical_id}`);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
});

Expand All @@ -197,7 +204,8 @@ export function addCanonicalCommands(program: Command): void {
fromId = await resolveCanonicalCompanyRef(from, canonRepo);
} catch (e) {
console.error(`error: ${(e as Error).message}`);
process.exit(1);
process.exitCode = 1;
return;
}
const aliasRepo = new CanonicalCompanyAliasRepo();
await aliasRepo.remove(fromId);
Expand Down
4 changes: 3 additions & 1 deletion src/cli/groups/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ export function addExtractorCommands(program: Command): void {
const out = (await withCli(new RetryDeadLettersTask()).run({ extractorId })) as {
eligibleAccessions: string[];
reprocessed: number;
failed: number;
};
const failedSuffix = out.failed > 0 ? `, ${out.failed} failed` : "";
console.log(
`reprocessed ${out.reprocessed} filing(s) from ${out.eligibleAccessions.length} eligible`
`reprocessed ${out.reprocessed} filing(s) from ${out.eligibleAccessions.length} eligible${failedSuffix}`
);
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/cli/groups/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ export function addInitCommand(parent: Command): void {
console.log("Database tables created.");

console.log("\nSetup complete! Next steps:");
console.log(" sec db status — verify database connection");
console.log(" sec bootstrap cik — download CIK name lookup");
console.log(" sec bootstrap index download filing indexes");
console.log(" sec db status — verify database connection");
console.log(" sec bootstrap download ciks — download the CIK name lookup");
console.log(" sec bootstrap ingest cik-namesload CIK names into the database");
} finally {
rl.close();
}
Expand Down
Loading