@useorgx/wizard 0.1.16 → 0.1.19

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/dist/cli.js CHANGED
@@ -1300,6 +1300,39 @@ function parseSkillFiles(value) {
1300
1300
  updatedAt: record.updatedAt.trim()
1301
1301
  };
1302
1302
  }
1303
+ function parsePeopleFirstCaptureEntry(value) {
1304
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1305
+ return void 0;
1306
+ }
1307
+ const record = value;
1308
+ if (!isNonEmptyString2(record.workspaceId) || !isNonEmptyString2(record.completedAt)) {
1309
+ return void 0;
1310
+ }
1311
+ return {
1312
+ workspaceId: record.workspaceId.trim(),
1313
+ completedAt: record.completedAt.trim(),
1314
+ ...isNonEmptyString2(record.personId) ? { personId: record.personId.trim() } : {},
1315
+ ...isNonEmptyString2(record.templatePersona) ? { templatePersona: record.templatePersona.trim() } : {},
1316
+ ...record.stagedLocally === true ? { stagedLocally: true } : {}
1317
+ };
1318
+ }
1319
+ function parsePeopleFirstCapture(value) {
1320
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1321
+ return void 0;
1322
+ }
1323
+ const record = value;
1324
+ if (!isNonEmptyString2(record.updatedAt) || !Array.isArray(record.entries)) {
1325
+ return void 0;
1326
+ }
1327
+ const entries = record.entries.map((entry) => parsePeopleFirstCaptureEntry(entry)).filter(
1328
+ (entry) => Boolean(entry)
1329
+ );
1330
+ if (entries.length === 0) return void 0;
1331
+ return {
1332
+ updatedAt: record.updatedAt.trim(),
1333
+ entries
1334
+ };
1335
+ }
1303
1336
  function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
1304
1337
  return {
1305
1338
  installationId: `wizard-${randomUUID()}`,
@@ -1314,6 +1347,7 @@ function sanitizeWizardStateRecord(record) {
1314
1347
  const firstValueInitiative = parseFirstValueInitiative(record.firstValueInitiative);
1315
1348
  const onboardingTask = parseOnboardingTask(record.onboardingTask);
1316
1349
  const skillFiles = parseSkillFiles(record.skillFiles);
1350
+ const peopleFirstCapture = parsePeopleFirstCapture(record.peopleFirstCapture);
1317
1351
  return {
1318
1352
  installationId: record.installationId.trim(),
1319
1353
  createdAt: record.createdAt.trim(),
@@ -1323,7 +1357,8 @@ function sanitizeWizardStateRecord(record) {
1323
1357
  ...demoInitiative ? { demoInitiative } : {},
1324
1358
  ...firstValueInitiative ? { firstValueInitiative } : {},
1325
1359
  ...onboardingTask ? { onboardingTask } : {},
1326
- ...skillFiles ? { skillFiles } : {}
1360
+ ...skillFiles ? { skillFiles } : {},
1361
+ ...peopleFirstCapture ? { peopleFirstCapture } : {}
1327
1362
  };
1328
1363
  }
1329
1364
  function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
@@ -1350,8 +1385,32 @@ function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
1350
1385
  if (onboardingTask !== void 0) state.onboardingTask = onboardingTask;
1351
1386
  const skillFiles = parseSkillFiles(parsed.skillFiles);
1352
1387
  if (skillFiles !== void 0) state.skillFiles = skillFiles;
1388
+ const peopleFirstCapture = parsePeopleFirstCapture(parsed.peopleFirstCapture);
1389
+ if (peopleFirstCapture !== void 0) state.peopleFirstCapture = peopleFirstCapture;
1353
1390
  return state;
1354
1391
  }
1392
+ function hasPeopleFirstCaptureCompleted(workspaceId, statePath = ORGX_WIZARD_STATE_PATH) {
1393
+ const state = readWizardState(statePath);
1394
+ if (!state?.peopleFirstCapture) return false;
1395
+ return state.peopleFirstCapture.entries.some(
1396
+ (entry) => entry.workspaceId === workspaceId
1397
+ );
1398
+ }
1399
+ function recordPeopleFirstCaptureCompletion(entry, statePath = ORGX_WIZARD_STATE_PATH) {
1400
+ return updateWizardState((current) => {
1401
+ const existingEntries = current.peopleFirstCapture?.entries ?? [];
1402
+ const withoutExisting = existingEntries.filter(
1403
+ (e) => e.workspaceId !== entry.workspaceId
1404
+ );
1405
+ return {
1406
+ ...current,
1407
+ peopleFirstCapture: {
1408
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1409
+ entries: [...withoutExisting, entry]
1410
+ }
1411
+ };
1412
+ }, statePath);
1413
+ }
1355
1414
  function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
1356
1415
  const record = sanitizeWizardStateRecord(value);
1357
1416
  writeJsonFile(statePath, record, { mode: 384 });
@@ -5525,6 +5584,536 @@ async function fetchOnboardingState(auth) {
5525
5584
  }
5526
5585
  }
5527
5586
 
5587
+ // src/lib/people-first-capture.ts
5588
+ var RELATIONSHIP_STAGE_OPTIONS = [
5589
+ { value: "stranger", label: "Stranger", hint: "no prior contact yet" },
5590
+ { value: "prospect", label: "Prospect", hint: "outreach planned or in flight (default)" },
5591
+ { value: "conversation", label: "Conversation", hint: "active 2-way dialogue" },
5592
+ { value: "design_partner", label: "Design partner", hint: "paid / equity track underway" },
5593
+ { value: "paused", label: "Paused", hint: "intentional pause, not wrong fit" },
5594
+ { value: "churned", label: "Churned", hint: "ended; history preserved" },
5595
+ { value: "alumni", label: "Alumni", hint: "closed engagement, may return" }
5596
+ ];
5597
+ var TRUST_TIER_OPTIONS = [
5598
+ { value: "cold", label: "Cold / unknown", hint: "new or barely-met" },
5599
+ { value: "warm", label: "Warm", hint: "know each other, some trust built" },
5600
+ { value: "close", label: "Close", hint: "deep trust \u2014 inner-circle candidate" }
5601
+ ];
5602
+ async function fetchPeopleFirstOnboardingState(auth) {
5603
+ try {
5604
+ const res = await fetch(
5605
+ buildOrgxApiUrl("/v1/people/onboarding", auth.baseUrl),
5606
+ {
5607
+ method: "GET",
5608
+ headers: { Authorization: `Bearer ${auth.apiKey}` },
5609
+ signal: AbortSignal.timeout(5e3)
5610
+ }
5611
+ );
5612
+ if (!res.ok) return null;
5613
+ const body = await res.json().catch(() => null);
5614
+ if (!body || !Array.isArray(body.workspaces)) return null;
5615
+ return body;
5616
+ } catch {
5617
+ return null;
5618
+ }
5619
+ }
5620
+ function parseContactChannels(raw) {
5621
+ const channels = [];
5622
+ const parts = raw.split(/[,\n]+/).map((part) => part.trim()).filter(Boolean);
5623
+ for (const part of parts) {
5624
+ if (/^https?:\/\/(?:www\.)?linkedin\.com\//i.test(part) || part.toLowerCase().startsWith("linkedin.com/")) {
5625
+ channels.push({ kind: "linkedin", value: part });
5626
+ } else if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(part)) {
5627
+ channels.push({ kind: "email", value: part });
5628
+ } else if (/^\+?[\d][\d\s()\-.]{5,}$/.test(part)) {
5629
+ channels.push({ kind: "phone", value: part.replace(/\s+/g, "") });
5630
+ } else {
5631
+ channels.push({ kind: "linkedin", value: part });
5632
+ }
5633
+ }
5634
+ return channels;
5635
+ }
5636
+ async function postJson(auth, path, body) {
5637
+ try {
5638
+ const res = await fetch(buildOrgxApiUrl(path, auth.baseUrl), {
5639
+ method: "POST",
5640
+ headers: {
5641
+ Authorization: `Bearer ${auth.apiKey}`,
5642
+ "Content-Type": "application/json"
5643
+ },
5644
+ body: JSON.stringify(body),
5645
+ signal: AbortSignal.timeout(1e4)
5646
+ });
5647
+ const text2 = await res.text().catch(() => "");
5648
+ let parsed = null;
5649
+ if (text2) {
5650
+ try {
5651
+ parsed = JSON.parse(text2);
5652
+ } catch {
5653
+ parsed = text2;
5654
+ }
5655
+ }
5656
+ if (!res.ok) {
5657
+ const detail = parsed && typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `HTTP ${res.status}`;
5658
+ return { ok: false, error: detail };
5659
+ }
5660
+ return { ok: true, status: res.status, body: parsed };
5661
+ } catch (err) {
5662
+ const message = err instanceof Error ? err.message : String(err);
5663
+ return { ok: false, error: message };
5664
+ }
5665
+ }
5666
+ function extractId(body) {
5667
+ if (body && typeof body === "object") {
5668
+ const candidate = body.id ?? body.data;
5669
+ if (typeof candidate === "string") return candidate;
5670
+ if (candidate && typeof candidate === "object") {
5671
+ const innerId = candidate.id;
5672
+ if (typeof innerId === "string") return innerId;
5673
+ }
5674
+ }
5675
+ return null;
5676
+ }
5677
+ var BACKEND_UNREACHABLE_HINT = "Could not sync with OrgX backend \u2014 check your network / run `orgx-wizard status`.";
5678
+ async function runPeopleFirstCapture(options) {
5679
+ if (!options.interactive) {
5680
+ return {
5681
+ status: "skipped_non_interactive",
5682
+ message: "People-first capture skipped \u2014 not attached to a TTY."
5683
+ };
5684
+ }
5685
+ if (!options.workspace) {
5686
+ return {
5687
+ status: "skipped_no_workspace",
5688
+ message: "People-first capture skipped \u2014 no workspace resolved."
5689
+ };
5690
+ }
5691
+ if (options.alreadyCompleted) {
5692
+ return {
5693
+ status: "skipped_already_completed",
5694
+ message: "People-first capture already completed for this workspace."
5695
+ };
5696
+ }
5697
+ const auth = await resolveOrgxAuth();
5698
+ if (!auth) {
5699
+ return {
5700
+ status: "failed",
5701
+ message: "People-first capture needs OrgX auth.",
5702
+ error: "no_auth"
5703
+ };
5704
+ }
5705
+ const state = await fetchPeopleFirstOnboardingState(auth);
5706
+ if (state) {
5707
+ const row = state.workspaces.find((w) => w.id === options.workspace.id);
5708
+ if (row?.peopleFirstCaptureCompletedAt) {
5709
+ return {
5710
+ status: "skipped_already_completed",
5711
+ message: "People-first capture already completed for this workspace."
5712
+ };
5713
+ }
5714
+ }
5715
+ const { prompts } = options;
5716
+ const proceed = await prompts.confirm({
5717
+ message: "Name the first person OrgX should know about? (takes ~30 seconds; OrgX will pre-draft a first-touch artifact for them)",
5718
+ initialValue: true
5719
+ });
5720
+ if (prompts.isCancel(proceed)) {
5721
+ return { status: "cancelled", message: "People-first capture cancelled." };
5722
+ }
5723
+ if (!proceed) {
5724
+ return {
5725
+ status: "skipped_declined",
5726
+ message: "Skipped \u2014 run the wizard again any time to add your first person."
5727
+ };
5728
+ }
5729
+ const headlineAnswer = await prompts.text({
5730
+ message: 'Who is this person to you? (one line \u2014 e.g. "prospect at Acme", "design partner", "old teammate")',
5731
+ placeholder: "design partner prospect for OrgX",
5732
+ validate(value) {
5733
+ if (!value || !value.trim()) return "Enter one line.";
5734
+ if (value.trim().length > 160) return "Keep it under 160 chars.";
5735
+ return void 0;
5736
+ }
5737
+ });
5738
+ if (prompts.isCancel(headlineAnswer)) {
5739
+ return { status: "cancelled", message: "People-first capture cancelled." };
5740
+ }
5741
+ const headline = typeof headlineAnswer === "string" ? headlineAnswer.trim() : "";
5742
+ const contextAnswer = await prompts.select({
5743
+ initialValue: "personal",
5744
+ message: "Is this on behalf of a business, or a personal relationship?",
5745
+ options: [
5746
+ { value: "personal", label: "Personal", hint: "no business attached yet" },
5747
+ { value: "business", label: "On behalf of a business", hint: "will also create a Business entity" }
5748
+ ]
5749
+ });
5750
+ if (prompts.isCancel(contextAnswer)) {
5751
+ return { status: "cancelled", message: "People-first capture cancelled." };
5752
+ }
5753
+ const hasBusiness = contextAnswer === "business";
5754
+ let businessName;
5755
+ if (hasBusiness) {
5756
+ const businessAnswer = await prompts.text({
5757
+ message: "Business name?",
5758
+ placeholder: "Acme Treasury",
5759
+ validate(value) {
5760
+ if (!value || !value.trim()) return "Enter the business name.";
5761
+ return void 0;
5762
+ }
5763
+ });
5764
+ if (prompts.isCancel(businessAnswer)) {
5765
+ return { status: "cancelled", message: "People-first capture cancelled." };
5766
+ }
5767
+ businessName = typeof businessAnswer === "string" ? businessAnswer.trim() : void 0;
5768
+ }
5769
+ const nameAnswer = await prompts.text({
5770
+ message: "Their name?",
5771
+ placeholder: "Laura Chen",
5772
+ validate(value) {
5773
+ if (!value || !value.trim()) return "Enter a display name.";
5774
+ return void 0;
5775
+ }
5776
+ });
5777
+ if (prompts.isCancel(nameAnswer)) {
5778
+ return { status: "cancelled", message: "People-first capture cancelled." };
5779
+ }
5780
+ const displayName = typeof nameAnswer === "string" ? nameAnswer.trim() : "";
5781
+ const contactAnswer = await prompts.text({
5782
+ message: "How do you reach them? (email, LinkedIn URL, or phone \u2014 comma-separated, at least one)",
5783
+ placeholder: "laura@acme.com, linkedin.com/in/laurachen",
5784
+ validate(value) {
5785
+ if (!value || !value.trim()) return "Enter at least one contact.";
5786
+ const parsed = parseContactChannels(value);
5787
+ if (parsed.length === 0) return "Could not parse any contact channels.";
5788
+ return void 0;
5789
+ }
5790
+ });
5791
+ if (prompts.isCancel(contactAnswer)) {
5792
+ return { status: "cancelled", message: "People-first capture cancelled." };
5793
+ }
5794
+ const contactChannels = parseContactChannels(
5795
+ typeof contactAnswer === "string" ? contactAnswer : ""
5796
+ );
5797
+ const stageAnswer = await prompts.select({
5798
+ initialValue: "prospect",
5799
+ message: "What stage is the relationship?",
5800
+ options: RELATIONSHIP_STAGE_OPTIONS.map((opt) => ({
5801
+ value: opt.value,
5802
+ label: opt.label,
5803
+ ...opt.hint ? { hint: opt.hint } : {}
5804
+ }))
5805
+ });
5806
+ if (prompts.isCancel(stageAnswer)) {
5807
+ return { status: "cancelled", message: "People-first capture cancelled." };
5808
+ }
5809
+ const relationshipStage = stageAnswer;
5810
+ const trustAnswer = await prompts.select({
5811
+ initialValue: "cold",
5812
+ message: "How would you describe the trust level right now?",
5813
+ options: TRUST_TIER_OPTIONS.map((opt) => ({
5814
+ value: opt.value,
5815
+ label: opt.label,
5816
+ ...opt.hint ? { hint: opt.hint } : {}
5817
+ }))
5818
+ });
5819
+ if (prompts.isCancel(trustAnswer)) {
5820
+ return { status: "cancelled", message: "People-first capture cancelled." };
5821
+ }
5822
+ const trustTier = trustAnswer;
5823
+ const innerCircleAnswer = await prompts.confirm({
5824
+ message: "Mark this person as inner-circle? (affects tone of drafted artifacts)",
5825
+ initialValue: false
5826
+ });
5827
+ if (prompts.isCancel(innerCircleAnswer)) {
5828
+ return { status: "cancelled", message: "People-first capture cancelled." };
5829
+ }
5830
+ const innerCircle = Boolean(innerCircleAnswer);
5831
+ const goalAnswer = await prompts.text({
5832
+ message: "Which matters most right now? (one line \u2014 this becomes their Goal)",
5833
+ placeholder: "agree on first pilot scope by May 3",
5834
+ validate(value) {
5835
+ if (!value || !value.trim()) return "Enter one line.";
5836
+ return void 0;
5837
+ }
5838
+ });
5839
+ if (prompts.isCancel(goalAnswer)) {
5840
+ return { status: "cancelled", message: "People-first capture cancelled." };
5841
+ }
5842
+ const goalTitle = typeof goalAnswer === "string" ? goalAnswer.trim() : "";
5843
+ let businessIntent;
5844
+ if (hasBusiness && relationshipStage === "conversation") {
5845
+ const intentAnswer = await prompts.select({
5846
+ initialValue: "client",
5847
+ message: "Is this person an investor in this business, or a client of it?",
5848
+ options: [
5849
+ { value: "client", label: "Client / prospect", hint: "default" },
5850
+ { value: "investor", label: "Investor / advisor" },
5851
+ { value: "unspecified", label: "Unsure / neither" }
5852
+ ]
5853
+ });
5854
+ if (prompts.isCancel(intentAnswer)) {
5855
+ return { status: "cancelled", message: "People-first capture cancelled." };
5856
+ }
5857
+ businessIntent = intentAnswer;
5858
+ }
5859
+ let businessId;
5860
+ if (hasBusiness && businessName) {
5861
+ const businessRes = await postJson(auth, "/v1/businesses", {
5862
+ display_name: businessName,
5863
+ relationship_stage: relationshipStage === "alumni" ? "alumni" : "engaged"
5864
+ });
5865
+ if (!businessRes.ok) {
5866
+ return {
5867
+ status: "failed",
5868
+ message: `${BACKEND_UNREACHABLE_HINT} (businesses: ${businessRes.error})`,
5869
+ error: businessRes.error
5870
+ };
5871
+ }
5872
+ businessId = extractId(businessRes.body) ?? void 0;
5873
+ }
5874
+ const personPayload = {
5875
+ display_name: displayName,
5876
+ headline,
5877
+ relationship_stage: relationshipStage,
5878
+ contact_channels: contactChannels,
5879
+ metadata: {
5880
+ captured_via: "orgx-wizard",
5881
+ inner_circle: innerCircle,
5882
+ trust_tier: trustTier,
5883
+ ...businessIntent ? { business_intent: businessIntent } : {}
5884
+ },
5885
+ ...businessId ? { business_id: businessId } : {}
5886
+ };
5887
+ const personRes = await postJson(auth, "/v1/people", personPayload);
5888
+ if (!personRes.ok) {
5889
+ return {
5890
+ status: "failed",
5891
+ message: `${BACKEND_UNREACHABLE_HINT} (people: ${personRes.error})`,
5892
+ error: personRes.error
5893
+ };
5894
+ }
5895
+ const personId = extractId(personRes.body) ?? void 0;
5896
+ if (!personId) {
5897
+ return {
5898
+ status: "failed",
5899
+ message: `${BACKEND_UNREACHABLE_HINT} (people: missing id in response)`,
5900
+ error: "missing_person_id"
5901
+ };
5902
+ }
5903
+ const goalRes = await postJson(auth, "/v1/goals", {
5904
+ owner_type: "person",
5905
+ owner_id: personId,
5906
+ title: goalTitle
5907
+ });
5908
+ if (!goalRes.ok) {
5909
+ return {
5910
+ status: "failed",
5911
+ message: `${BACKEND_UNREACHABLE_HINT} (goals: ${goalRes.error})`,
5912
+ error: goalRes.error
5913
+ };
5914
+ }
5915
+ const goalId = extractId(goalRes.body) ?? void 0;
5916
+ if (!goalId) {
5917
+ return {
5918
+ status: "failed",
5919
+ message: `${BACKEND_UNREACHABLE_HINT} (goals: missing id in response)`,
5920
+ error: "missing_goal_id"
5921
+ };
5922
+ }
5923
+ const person = {
5924
+ id: personId,
5925
+ display_name: displayName,
5926
+ headline,
5927
+ relationship_stage: relationshipStage,
5928
+ contact_channels: contactChannels,
5929
+ inner_circle: innerCircle,
5930
+ trust_tier: trustTier,
5931
+ ...businessId ? { business_id: businessId } : {},
5932
+ ...businessIntent ? { business_intent: businessIntent } : {}
5933
+ };
5934
+ const goal = {
5935
+ id: goalId,
5936
+ owner_type: "person",
5937
+ owner_id: personId,
5938
+ title: goalTitle
5939
+ };
5940
+ return {
5941
+ status: "completed",
5942
+ message: `Captured ${displayName} in your workspace.`,
5943
+ person,
5944
+ goal
5945
+ };
5946
+ }
5947
+
5948
+ // src/peopleFirst/templateSelect.ts
5949
+ function selectPeopleFirstTemplate(input) {
5950
+ const trustTier = input.trustTier ?? "cold";
5951
+ if (input.innerCircle) {
5952
+ return {
5953
+ persona: "inner_circle",
5954
+ reason: "inner-circle mark set by user",
5955
+ needsBusinessIntentPrompt: false
5956
+ };
5957
+ }
5958
+ switch (input.relationshipStage) {
5959
+ case "stranger":
5960
+ case "prospect":
5961
+ return {
5962
+ persona: "cold_outreach",
5963
+ reason: `stage=${input.relationshipStage} \u2014 cold outreach draft`,
5964
+ needsBusinessIntentPrompt: false
5965
+ };
5966
+ case "conversation": {
5967
+ if (input.hasBusiness) {
5968
+ if (input.businessIntent === "investor") {
5969
+ return {
5970
+ persona: "investor_prep",
5971
+ reason: "conversation + business + investor intent",
5972
+ needsBusinessIntentPrompt: false
5973
+ };
5974
+ }
5975
+ if (input.businessIntent === "client") {
5976
+ return {
5977
+ persona: "client_trust",
5978
+ reason: "conversation + business + client intent",
5979
+ needsBusinessIntentPrompt: false
5980
+ };
5981
+ }
5982
+ return {
5983
+ persona: "client_trust",
5984
+ reason: "conversation + business \u2014 defaulting to client_trust; ask to confirm",
5985
+ needsBusinessIntentPrompt: true
5986
+ };
5987
+ }
5988
+ if (trustTier === "warm" || trustTier === "close") {
5989
+ return {
5990
+ persona: "founder_ally",
5991
+ reason: `conversation + trust=${trustTier} \u2014 founder-ally outreach`,
5992
+ needsBusinessIntentPrompt: false
5993
+ };
5994
+ }
5995
+ return {
5996
+ persona: "cold_outreach",
5997
+ reason: "conversation + cold trust \u2014 treating as cold_outreach",
5998
+ needsBusinessIntentPrompt: false
5999
+ };
6000
+ }
6001
+ case "design_partner":
6002
+ case "active_client":
6003
+ return {
6004
+ persona: "client_trust",
6005
+ reason: `stage=${input.relationshipStage} \u2014 client_trust cadence`,
6006
+ needsBusinessIntentPrompt: false
6007
+ };
6008
+ case "alumni":
6009
+ return {
6010
+ persona: "alumni_touch",
6011
+ reason: "stage=alumni \u2014 alumni_touch rekindle",
6012
+ needsBusinessIntentPrompt: false
6013
+ };
6014
+ case "paused":
6015
+ case "churned":
6016
+ return {
6017
+ persona: "alumni_touch",
6018
+ reason: `stage=${input.relationshipStage} \u2014 treating as alumni_touch for rekindle tone`,
6019
+ needsBusinessIntentPrompt: false
6020
+ };
6021
+ }
6022
+ }
6023
+
6024
+ // src/lib/people-first-artifact.ts
6025
+ async function callDraftEndpoint(auth, body) {
6026
+ try {
6027
+ const res = await fetch(buildOrgxApiUrl("/v1/artifacts/draft", auth.baseUrl), {
6028
+ method: "POST",
6029
+ headers: {
6030
+ Authorization: `Bearer ${auth.apiKey}`,
6031
+ "Content-Type": "application/json"
6032
+ },
6033
+ body: JSON.stringify(body),
6034
+ signal: AbortSignal.timeout(1e4)
6035
+ });
6036
+ if (!res.ok) {
6037
+ return { ok: false, error: `HTTP ${res.status}` };
6038
+ }
6039
+ const data = await res.json().catch(() => null);
6040
+ return { ok: true, data: data ?? {} };
6041
+ } catch (err) {
6042
+ const message = err instanceof Error ? err.message : String(err);
6043
+ return { ok: false, error: message };
6044
+ }
6045
+ }
6046
+ async function runPeopleFirstArtifactDraft(options) {
6047
+ if (!options.interactive) {
6048
+ return {
6049
+ status: "skipped_non_interactive",
6050
+ message: "Artifact draft skipped \u2014 not attached to a TTY."
6051
+ };
6052
+ }
6053
+ const auth = await resolveOrgxAuth();
6054
+ if (!auth) {
6055
+ return {
6056
+ status: "failed",
6057
+ message: "Artifact draft needs OrgX auth.",
6058
+ error: "no_auth"
6059
+ };
6060
+ }
6061
+ let businessIntent = options.businessIntent;
6062
+ const { prompts, person } = options;
6063
+ const hasBusiness = Boolean(options.businessName || person.business_id);
6064
+ const preselection = selectPeopleFirstTemplate({
6065
+ relationshipStage: person.relationship_stage,
6066
+ hasBusiness,
6067
+ trustTier: person.trust_tier,
6068
+ innerCircle: person.inner_circle,
6069
+ ...businessIntent ? { businessIntent } : {}
6070
+ });
6071
+ let persona = preselection.persona;
6072
+ if (preselection.needsBusinessIntentPrompt && !businessIntent) {
6073
+ const answer = await prompts.select({
6074
+ initialValue: "client",
6075
+ message: "Is this more of a client-trust update, or investor-prep?",
6076
+ options: [
6077
+ { value: "client", label: "Client trust (default)" },
6078
+ { value: "investor", label: "Investor prep" }
6079
+ ]
6080
+ });
6081
+ if (prompts.isCancel(answer)) {
6082
+ return { status: "cancelled", message: "Artifact draft cancelled." };
6083
+ }
6084
+ businessIntent = answer;
6085
+ persona = selectPeopleFirstTemplate({
6086
+ relationshipStage: person.relationship_stage,
6087
+ hasBusiness,
6088
+ trustTier: person.trust_tier,
6089
+ innerCircle: person.inner_circle,
6090
+ businessIntent
6091
+ }).persona;
6092
+ }
6093
+ const serverResult = await callDraftEndpoint(auth, {
6094
+ template_persona: persona,
6095
+ person_id: person.id,
6096
+ calibration_inputs: {
6097
+ voice_examples: []
6098
+ }
6099
+ });
6100
+ if (!serverResult.ok) {
6101
+ return {
6102
+ status: "failed",
6103
+ message: `Could not draft artifact \u2014 ${serverResult.error}. Check your network / run \`orgx-wizard status\`.`,
6104
+ persona,
6105
+ error: serverResult.error
6106
+ };
6107
+ }
6108
+ return {
6109
+ status: "drafted",
6110
+ message: `Drafted ${persona} artifact for ${person.display_name}.`,
6111
+ persona,
6112
+ ...serverResult.data.artifact_id ? { serverArtifactId: serverResult.data.artifact_id } : {},
6113
+ ...serverResult.data.url ? { serverArtifactUrl: serverResult.data.url } : {}
6114
+ };
6115
+ }
6116
+
5528
6117
  // src/spinner.ts
5529
6118
  import ora from "ora";
5530
6119
  import pc2 from "picocolors";
@@ -6174,6 +6763,109 @@ async function maybeInstallOptionalCompanionPlugins(input) {
6174
6763
  ...input.telemetry ? { telemetry: input.telemetry } : {}
6175
6764
  });
6176
6765
  }
6766
+ async function maybeRunPeopleFirstCapture(input) {
6767
+ if (!input.interactive || !input.workspace) {
6768
+ return "skipped";
6769
+ }
6770
+ const alreadyCompleted = hasPeopleFirstCaptureCompleted(input.workspace.id);
6771
+ const captureResult = await runPeopleFirstCapture({
6772
+ interactive: input.interactive,
6773
+ workspace: input.workspace,
6774
+ alreadyCompleted,
6775
+ prompts: {
6776
+ cancel: clack.cancel,
6777
+ isCancel: clack.isCancel,
6778
+ text: textPrompt,
6779
+ select: selectPrompt,
6780
+ confirm: clack.confirm
6781
+ }
6782
+ });
6783
+ switch (captureResult.status) {
6784
+ case "skipped_already_completed":
6785
+ return "skipped";
6786
+ case "skipped_non_interactive":
6787
+ case "skipped_no_workspace":
6788
+ return "skipped";
6789
+ case "skipped_declined":
6790
+ console.log(` ${ICON.skip} ${pc3.dim(captureResult.message)}`);
6791
+ return "skipped";
6792
+ case "cancelled":
6793
+ return "cancelled";
6794
+ case "failed":
6795
+ console.log(` ${ICON.warn} ${pc3.yellow("people-first")} ${pc3.dim(captureResult.message)}`);
6796
+ return "failed";
6797
+ case "completed":
6798
+ break;
6799
+ }
6800
+ if (!captureResult.person || !captureResult.goal) {
6801
+ return "failed";
6802
+ }
6803
+ console.log(
6804
+ ` ${ICON.ok} ${pc3.green("people-first")} ${pc3.dim(
6805
+ `Captured ${captureResult.person.display_name} (${captureResult.person.relationship_stage}).`
6806
+ )}`
6807
+ );
6808
+ const draftResult = await runPeopleFirstArtifactDraft({
6809
+ interactive: input.interactive,
6810
+ person: captureResult.person,
6811
+ prompts: {
6812
+ cancel: clack.cancel,
6813
+ isCancel: clack.isCancel,
6814
+ text: textPrompt,
6815
+ select: selectPrompt,
6816
+ confirm: clack.confirm
6817
+ }
6818
+ });
6819
+ if (draftResult.status === "cancelled") {
6820
+ } else if (draftResult.status === "failed") {
6821
+ console.log(
6822
+ ` ${ICON.warn} ${pc3.yellow("artifact draft")} ${pc3.dim(draftResult.message)}`
6823
+ );
6824
+ } else if (draftResult.status === "drafted") {
6825
+ console.log(
6826
+ ` ${ICON.ok} ${pc3.green("artifact draft")} ${pc3.dim(
6827
+ `OrgX pre-drafted a ${draftResult.persona ?? "first-touch"} artifact.`
6828
+ )}`
6829
+ );
6830
+ }
6831
+ try {
6832
+ recordPeopleFirstCaptureCompletion({
6833
+ workspaceId: input.workspace.id,
6834
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
6835
+ ...captureResult.person ? { personId: captureResult.person.id } : {},
6836
+ ...draftResult.persona ? { templatePersona: draftResult.persona } : {}
6837
+ });
6838
+ } catch {
6839
+ }
6840
+ const baseUrl = process.env.ORGX_APP_URL?.trim() || DEFAULT_ORGX_BASE_URL;
6841
+ const commandUrl = `${baseUrl.replace(/\/+$/, "")}/command`;
6842
+ console.log("");
6843
+ console.log(
6844
+ ` ${ICON.ok} ${pc3.bold(
6845
+ `You brought in ${captureResult.person.display_name}.`
6846
+ )} ${pc3.dim(
6847
+ `Head to ${commandUrl} to see them in your people list \u2014 OrgX already drafted a first-touch artifact.`
6848
+ )}`
6849
+ );
6850
+ if (input.openInBrowser) {
6851
+ const openResult = openBrowser(commandUrl);
6852
+ if (!openResult.ok && openResult.error) {
6853
+ console.log(` ${pc3.dim(`(Could not auto-open browser: ${openResult.error})`)}`);
6854
+ }
6855
+ } else {
6856
+ const openAnswer = await clack.confirm({
6857
+ message: `Open ${commandUrl} in your browser now?`,
6858
+ initialValue: true
6859
+ });
6860
+ if (!clack.isCancel(openAnswer) && openAnswer === true) {
6861
+ const openResult = openBrowser(commandUrl);
6862
+ if (!openResult.ok && openResult.error) {
6863
+ console.log(` ${pc3.dim(`(Could not auto-open browser: ${openResult.error})`)}`);
6864
+ }
6865
+ }
6866
+ }
6867
+ return "completed";
6868
+ }
6177
6869
  function printAuthStatus(status) {
6178
6870
  if (!status.configured) {
6179
6871
  console.log(` ${ICON.warn} ${pc3.yellow("no account")} run ${pc3.cyan(`${getCmd()} auth login`)} to connect`);
@@ -6252,12 +6944,12 @@ function printDoctorReport(report, assessment) {
6252
6944
  async function main() {
6253
6945
  const program = new Command();
6254
6946
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
6255
- const pkgVersion = true ? "0.1.16" : void 0;
6947
+ const pkgVersion = true ? "0.1.19" : void 0;
6256
6948
  program.version(pkgVersion ?? "unknown", "-V, --version");
6257
6949
  program.hook("preAction", () => {
6258
6950
  console.log(renderBanner(pkgVersion));
6259
6951
  });
6260
- program.command("setup").description("Add OrgX MCP configs, skills/rules, and companion plugins to detected tools.").option("--preset <name>", "run a setup bundle (currently: founder)").action(async (options) => {
6952
+ program.command("setup").description("Add OrgX MCP configs, skills/rules, and companion plugins to detected tools.").option("--preset <name>", "run a setup bundle (currently: founder)").option("--open", "After people-first capture, auto-open the OrgX dashboard in your browser.", false).action(async (options) => {
6261
6953
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
6262
6954
  await safeTrackWizardTelemetry("wizard_started", {
6263
6955
  command: "setup",
@@ -6463,6 +7155,14 @@ async function main() {
6463
7155
  } else if (briefResult.status === "failed") {
6464
7156
  console.log(` ${ICON.warn} ${pc3.yellow("daily brief")} ${pc3.dim(briefResult.message)}`);
6465
7157
  }
7158
+ const peopleFirstResult = await maybeRunPeopleFirstCapture({
7159
+ interactive,
7160
+ openInBrowser: Boolean(options.open),
7161
+ workspace: resolvedWorkspace
7162
+ });
7163
+ if (peopleFirstResult === "cancelled") {
7164
+ return;
7165
+ }
6466
7166
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
6467
7167
  interactive,
6468
7168
  telemetry: { command: "setup", preset: "standard" },