@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 +703 -3
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
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.
|
|
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" },
|