@fairmint/captable-ai 0.1.9 → 0.1.11

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.
Files changed (2) hide show
  1. package/dist/index.js +203 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -599,6 +599,22 @@ var COMMAND_REGISTRY = [
599
599
  }
600
600
  ]
601
601
  },
602
+ {
603
+ name: "objects review-non-cash",
604
+ group: "captable",
605
+ description: "List unmarked convertible issuances whose stored evidence suggests possible non-cash consideration.",
606
+ usage: "captable-ai objects review-non-cash [--status draft|committed] [--json]",
607
+ requiresAuth: true,
608
+ requiresPortal: true,
609
+ flags: [
610
+ {
611
+ name: "status",
612
+ description: "Review only latest draft or committed objects.",
613
+ type: "string",
614
+ enum: ["draft", "committed"]
615
+ }
616
+ ]
617
+ },
602
618
  {
603
619
  name: "objects get",
604
620
  group: "captable",
@@ -16559,6 +16575,31 @@ var snapshotMetricsSchema = external_exports.object({
16559
16575
  unallocated: external_exports.number()
16560
16576
  }).nullable()
16561
16577
  });
16578
+ var warrantResolutionItemSchema = external_exports.object({
16579
+ securityId: external_exports.string(),
16580
+ stakeholderId: external_exports.string().nullable(),
16581
+ stakeholderName: external_exports.string().nullable(),
16582
+ active: external_exports.boolean(),
16583
+ warrantUnitStatus: external_exports.enum(["known", "unresolved"]),
16584
+ warrantUnits: external_exports.number().nullable(),
16585
+ warrantUnitSource: external_exports.literal("issuance_quantity").nullable(),
16586
+ shareQuantityStatus: external_exports.enum(["known", "deferred", "unresolved"]),
16587
+ shareQuantity: external_exports.number().nullable(),
16588
+ shareQuantitySource: external_exports.enum(["issuance_quantity_1_to_1", "converts_to_quantity", "fixed_percent"]).nullable(),
16589
+ formulaText: external_exports.string().nullable(),
16590
+ exercisePrice: external_exports.object({
16591
+ amount: external_exports.string(),
16592
+ currency: external_exports.string()
16593
+ }).nullable(),
16594
+ relatedSecurityId: external_exports.string().nullable()
16595
+ });
16596
+ var warrantsSnapshotSchema = external_exports.object({
16597
+ numberOfSecurities: external_exports.number().int().nonnegative(),
16598
+ unresolvedCount: external_exports.number().int().nonnegative(),
16599
+ deferredCount: external_exports.number().int().nonnegative(),
16600
+ totalUnderlyingShares: external_exports.number().nonnegative(),
16601
+ items: external_exports.array(warrantResolutionItemSchema).optional()
16602
+ });
16562
16603
  var snapshotSchema = external_exports.object({
16563
16604
  status: snapshotStatusSchema,
16564
16605
  locale: snapshotLocaleSchema,
@@ -16569,6 +16610,7 @@ var snapshotSchema = external_exports.object({
16569
16610
  stockClasses: external_exports.array(external_exports.unknown())
16570
16611
  }).nullable(),
16571
16612
  convertibles: convertiblesPayloadSchema.nullable(),
16613
+ warrants: warrantsSnapshotSchema,
16572
16614
  metrics: snapshotMetricsSchema
16573
16615
  });
16574
16616
 
@@ -17003,6 +17045,27 @@ Human table sorts lowest extraction confidence first. Conf markers:
17003
17045
 
17004
17046
  Global:
17005
17047
  --json Machine-readable JSON output (API order, not confidence-sorted)`;
17048
+ var HELP_OBJECTS_REVIEW_NON_CASH = `objects review-non-cash \u2014 Review possible unclassified non-cash convertibles
17049
+
17050
+ Builds a read-only, portal-wide review queue from unmarked convertible issuances
17051
+ whose stored consideration text, comments, or source-document name mentions
17052
+ services or other possible non-cash consideration. Matches are evidence for human
17053
+ review only: this command never classifies, patches, or commits an OCF object.
17054
+
17055
+ Usage:
17056
+ captable-ai objects review-non-cash [--status draft|committed] [--json]
17057
+
17058
+ Options:
17059
+ --status STATUS Review only latest draft or committed objects
17060
+
17061
+ Workflow:
17062
+ 1. Run with --status committed --json and review each source agreement.
17063
+ 2. For confirmed wholly non-cash issuances, patch only consideration_text with
17064
+ NON_CASH: <document-verbatim description> using the reported object version.
17065
+ 3. Dry-run and commit exactly the reviewed object IDs together.
17066
+
17067
+ Global:
17068
+ --json Machine-readable output including evidence and object version`;
17006
17069
  var HELP_OBJECTS_GET = `objects get \u2014 Show one OCF object
17007
17070
 
17008
17071
  After the OCF payload, prints a one-line vesting summary when the object (or
@@ -17120,6 +17183,8 @@ var HELP_OBJECTS_GROUP = `objects \u2014 OCF objects
17120
17183
 
17121
17184
  ${HELP_OBJECTS_LIST}
17122
17185
 
17186
+ ${HELP_OBJECTS_REVIEW_NON_CASH}
17187
+
17123
17188
  ${HELP_OBJECTS_GET}
17124
17189
 
17125
17190
  ${HELP_OBJECTS_DUMP}
@@ -20141,6 +20206,136 @@ Reverted ${res.data.revertedCount} draft object(s).
20141
20206
  `);
20142
20207
  }
20143
20208
 
20209
+ // src/commands/objects-review-non-cash.ts
20210
+ var NON_CASH_PREFIX = "NON_CASH:";
20211
+ var POSSIBLE_NON_CASH_TERMS = /\b(?:non[- ]cash|services?|consulting|advisory|advisor|intellectual property|ip assignment|debt forgiveness|in[- ]kind)\b/i;
20212
+ function stringValue(value) {
20213
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
20214
+ }
20215
+ function collectCommentText(value) {
20216
+ if (typeof value === "string") return value.trim() ? [value.trim()] : [];
20217
+ if (!Array.isArray(value)) return [];
20218
+ return value.flatMap((comment) => {
20219
+ if (typeof comment === "string") return comment.trim() ? [comment.trim()] : [];
20220
+ if (!comment || typeof comment !== "object") return [];
20221
+ const text = Reflect.get(comment, "text") ?? Reflect.get(comment, "comment");
20222
+ return typeof text === "string" && text.trim() ? [text.trim()] : [];
20223
+ });
20224
+ }
20225
+ function hasValidNonCashMarker(considerationText) {
20226
+ if (typeof considerationText !== "string") return false;
20227
+ const trimmed = considerationText.trimStart();
20228
+ if (!trimmed.startsWith(NON_CASH_PREFIX)) return false;
20229
+ return trimmed.slice(NON_CASH_PREFIX.length).trim().length > 0;
20230
+ }
20231
+ function findNonCashReviewCandidates(objects, documents) {
20232
+ const documentsById = new Map(documents.map((document) => [document.id, document]));
20233
+ const candidates = [];
20234
+ for (const object2 of objects) {
20235
+ if (object2.subtype !== "TX_CONVERTIBLE_ISSUANCE") continue;
20236
+ if (hasValidNonCashMarker(object2.ocf.consideration_text)) continue;
20237
+ const evidence = [];
20238
+ const considerationText = stringValue(object2.ocf.consideration_text);
20239
+ if (considerationText && (considerationText.startsWith(NON_CASH_PREFIX) || POSSIBLE_NON_CASH_TERMS.test(considerationText))) {
20240
+ evidence.push({ field: "consideration_text", text: considerationText });
20241
+ }
20242
+ for (const comment of collectCommentText(object2.ocf.comments)) {
20243
+ if (POSSIBLE_NON_CASH_TERMS.test(comment)) {
20244
+ evidence.push({ field: "comments", text: comment });
20245
+ }
20246
+ }
20247
+ const document = object2.documentId ? documentsById.get(object2.documentId) : void 0;
20248
+ if (document && POSSIBLE_NON_CASH_TERMS.test(document.name)) {
20249
+ evidence.push({ field: "document", text: document.name });
20250
+ }
20251
+ if (evidence.length === 0) continue;
20252
+ candidates.push({
20253
+ objectId: object2.id,
20254
+ version: object2.version,
20255
+ status: object2.status,
20256
+ securityId: stringValue(object2.ocf.security_id),
20257
+ customId: stringValue(object2.ocf.custom_id),
20258
+ investmentAmount: object2.ocf.investment_amount ?? null,
20259
+ considerationText,
20260
+ documentId: object2.documentId ?? null,
20261
+ documentName: document?.name ?? null,
20262
+ evidence
20263
+ });
20264
+ }
20265
+ return candidates.sort((left, right) => left.objectId.localeCompare(right.objectId));
20266
+ }
20267
+ async function runObjectsReviewNonCash(args, rawArgv) {
20268
+ if (argvRequestsHelp(rawArgv)) {
20269
+ process.stdout.write(`${HELP_OBJECTS_REVIEW_NON_CASH}
20270
+ `);
20271
+ process.exit(0);
20272
+ }
20273
+ validateFlags(args, { flags: ["status"] });
20274
+ if (args.positional.length > 0) {
20275
+ exitWithError(
20276
+ `Unexpected argument: ${args.positional[0]}. This command takes no positional arguments.`,
20277
+ "UNEXPECTED_ARGUMENT",
20278
+ void 0,
20279
+ EXIT.USAGE
20280
+ );
20281
+ }
20282
+ const status = getFlagValue(args, "status");
20283
+ if (status !== void 0 && status !== "draft" && status !== "committed") {
20284
+ exitWithError(
20285
+ `--status must be one of: draft, committed (got: "${status}")`,
20286
+ "VALIDATION_FAILED",
20287
+ void 0,
20288
+ EXIT.VALIDATION
20289
+ );
20290
+ }
20291
+ const portalId = await getActivePortalId();
20292
+ const page = await fetchAllObjectPages(portalId, {
20293
+ subtype: "TX_CONVERTIBLE_ISSUANCE",
20294
+ status
20295
+ });
20296
+ const candidates = findNonCashReviewCandidates(page.data.objects, page.data.documents);
20297
+ if (isJsonMode()) {
20298
+ printJson({
20299
+ data: {
20300
+ candidates,
20301
+ reviewedCount: page.data.objects.length
20302
+ },
20303
+ meta: {
20304
+ candidateCount: candidates.length,
20305
+ classification: "human_review_required"
20306
+ }
20307
+ });
20308
+ return;
20309
+ }
20310
+ printLine(
20311
+ `
20312
+ Possible unclassified non-cash convertible consideration (${candidates.length} found):
20313
+ `
20314
+ );
20315
+ printTable(
20316
+ candidates.map((candidate) => ({
20317
+ id: candidate.objectId,
20318
+ version: candidate.version,
20319
+ security: candidate.customId ?? candidate.securityId ?? "",
20320
+ amount: candidate.investmentAmount && typeof candidate.investmentAmount === "object" ? Reflect.get(candidate.investmentAmount, "amount") ?? "" : "",
20321
+ document: candidate.documentName ?? candidate.documentId ?? "",
20322
+ evidence: candidate.evidence.map((item) => item.field).join(", ")
20323
+ })),
20324
+ [
20325
+ { key: "id", label: "Object ID", width: 38 },
20326
+ { key: "version", label: "Version", width: 8 },
20327
+ { key: "security", label: "Security", width: 18 },
20328
+ { key: "amount", label: "Amount", width: 12 },
20329
+ { key: "document", label: "Document", width: 24 },
20330
+ { key: "evidence", label: "Evidence", width: 28 }
20331
+ ]
20332
+ );
20333
+ printLine(
20334
+ "\nReview the source agreement before patching. This command never classifies or writes objects."
20335
+ );
20336
+ printLine();
20337
+ }
20338
+
20144
20339
  // src/commands/objects-update.ts
20145
20340
  import { readFileSync as readFileSync2 } from "fs";
20146
20341
  async function readStdin2() {
@@ -21231,6 +21426,7 @@ Cap Table (read):
21231
21426
  Review: --stakeholder, --document, --max-confidence N (requires --all)
21232
21427
  Pagination: --limit (1-250), --cursor, --all
21233
21428
  Dates: --from / --to as YYYY-MM-DD
21429
+ objects review-non-cash Find unmarked convertibles that need consideration review
21234
21430
  objects get <id> Show a single OCF object (vesting summary when present)
21235
21431
  objects dump Engine-shaped ocf.json (default --status committed; --output path)
21236
21432
  schemas list List OCF object types
@@ -21528,6 +21724,12 @@ async function main() {
21528
21724
  case "patch":
21529
21725
  await runObjectsPatch(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21530
21726
  break;
21727
+ case "review-non-cash":
21728
+ await runObjectsReviewNonCash(
21729
+ parseArgs(positionalArgs.slice(2)),
21730
+ positionalArgs.slice(2)
21731
+ );
21732
+ break;
21531
21733
  case "revert":
21532
21734
  await runObjectsRevert(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21533
21735
  break;
@@ -21539,7 +21741,7 @@ async function main() {
21539
21741
  break;
21540
21742
  default:
21541
21743
  exitWithError(
21542
- `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | get | dump | update | patch | revert | versions | delete`,
21744
+ `Unknown objects command: ${sub ?? "(none)"}. Try: objects list | review-non-cash | get | dump | update | patch | revert | versions | delete`,
21543
21745
  "UNKNOWN_COMMAND",
21544
21746
  void 0,
21545
21747
  EXIT.USAGE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairmint/captable-ai",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Agent-native CLI for Captable AI — manage companies, upload documents, run OCF extraction, and commit cap tables from the terminal.",
5
5
  "keywords": [
6
6
  "captable",