@fairmint/captable-ai 0.1.10 → 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 +177 -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",
@@ -17029,6 +17045,27 @@ Human table sorts lowest extraction confidence first. Conf markers:
17029
17045
 
17030
17046
  Global:
17031
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`;
17032
17069
  var HELP_OBJECTS_GET = `objects get \u2014 Show one OCF object
17033
17070
 
17034
17071
  After the OCF payload, prints a one-line vesting summary when the object (or
@@ -17146,6 +17183,8 @@ var HELP_OBJECTS_GROUP = `objects \u2014 OCF objects
17146
17183
 
17147
17184
  ${HELP_OBJECTS_LIST}
17148
17185
 
17186
+ ${HELP_OBJECTS_REVIEW_NON_CASH}
17187
+
17149
17188
  ${HELP_OBJECTS_GET}
17150
17189
 
17151
17190
  ${HELP_OBJECTS_DUMP}
@@ -20167,6 +20206,136 @@ Reverted ${res.data.revertedCount} draft object(s).
20167
20206
  `);
20168
20207
  }
20169
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
+
20170
20339
  // src/commands/objects-update.ts
20171
20340
  import { readFileSync as readFileSync2 } from "fs";
20172
20341
  async function readStdin2() {
@@ -21257,6 +21426,7 @@ Cap Table (read):
21257
21426
  Review: --stakeholder, --document, --max-confidence N (requires --all)
21258
21427
  Pagination: --limit (1-250), --cursor, --all
21259
21428
  Dates: --from / --to as YYYY-MM-DD
21429
+ objects review-non-cash Find unmarked convertibles that need consideration review
21260
21430
  objects get <id> Show a single OCF object (vesting summary when present)
21261
21431
  objects dump Engine-shaped ocf.json (default --status committed; --output path)
21262
21432
  schemas list List OCF object types
@@ -21554,6 +21724,12 @@ async function main() {
21554
21724
  case "patch":
21555
21725
  await runObjectsPatch(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21556
21726
  break;
21727
+ case "review-non-cash":
21728
+ await runObjectsReviewNonCash(
21729
+ parseArgs(positionalArgs.slice(2)),
21730
+ positionalArgs.slice(2)
21731
+ );
21732
+ break;
21557
21733
  case "revert":
21558
21734
  await runObjectsRevert(parseArgs(positionalArgs.slice(2)), positionalArgs.slice(2));
21559
21735
  break;
@@ -21565,7 +21741,7 @@ async function main() {
21565
21741
  break;
21566
21742
  default:
21567
21743
  exitWithError(
21568
- `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`,
21569
21745
  "UNKNOWN_COMMAND",
21570
21746
  void 0,
21571
21747
  EXIT.USAGE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairmint/captable-ai",
3
- "version": "0.1.10",
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",