@useorgx/wizard 0.1.20 → 0.1.21

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
@@ -1389,28 +1389,6 @@ function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
1389
1389
  if (peopleFirstCapture !== void 0) state.peopleFirstCapture = peopleFirstCapture;
1390
1390
  return state;
1391
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
- }
1414
1392
  function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
1415
1393
  const record = sanitizeWizardStateRecord(value);
1416
1394
  writeJsonFile(statePath, record, { mode: 384 });
@@ -5403,7 +5381,357 @@ async function trackWizardTelemetry(event, properties = {}, options = {}) {
5403
5381
  return response?.ok === true;
5404
5382
  }
5405
5383
 
5384
+ // src/lib/intents.ts
5385
+ var AGENT_SLUGS = [
5386
+ "mark",
5387
+ "eli",
5388
+ "dana",
5389
+ "pace",
5390
+ "sage",
5391
+ "orion",
5392
+ "xandy"
5393
+ ];
5394
+ var INTENT_STATUSES = [
5395
+ "pending",
5396
+ "approved",
5397
+ "adjusted",
5398
+ "archived"
5399
+ ];
5400
+ var AGENT_ROLES = {
5401
+ mark: "Partnerships",
5402
+ eli: "Engineering",
5403
+ dana: "Brand",
5404
+ pace: "Product",
5405
+ sage: "Strategy",
5406
+ orion: "Routing",
5407
+ xandy: "Default"
5408
+ };
5409
+ var CONFIDENCE_DECIMAL = {
5410
+ low: 0.55,
5411
+ med: 0.78,
5412
+ high: 0.92
5413
+ };
5414
+ var AGENT_ANSI_256 = {
5415
+ pace: 34,
5416
+ // green
5417
+ eli: 44,
5418
+ // cyan
5419
+ mark: 208,
5420
+ // orange
5421
+ sage: 135,
5422
+ // purple
5423
+ orion: 214,
5424
+ // amber
5425
+ dana: 199,
5426
+ // pink
5427
+ xandy: 37
5428
+ // teal
5429
+ };
5430
+ function parseAgentSlug(value) {
5431
+ if (typeof value !== "string") return null;
5432
+ return AGENT_SLUGS.includes(value) ? value : null;
5433
+ }
5434
+ function parseStatus(value) {
5435
+ if (typeof value !== "string") return null;
5436
+ return INTENT_STATUSES.includes(value) ? value : null;
5437
+ }
5438
+ function parseConfidence(value) {
5439
+ if (value === "low" || value === "med" || value === "high") return value;
5440
+ return "med";
5441
+ }
5442
+ function parsePlanStep(value) {
5443
+ if (!isRecord(value)) return null;
5444
+ const title = typeof value.title === "string" ? value.title.trim() : "";
5445
+ if (!title) return null;
5446
+ const detail = typeof value.detail === "string" && value.detail.trim().length > 0 ? value.detail : void 0;
5447
+ const agentSlug = parseAgentSlug(value.agent_slug);
5448
+ const step = { title };
5449
+ if (detail !== void 0) step.detail = detail;
5450
+ if (agentSlug !== null) step.agent_slug = agentSlug;
5451
+ return step;
5452
+ }
5453
+ function parsePlanPreview(value) {
5454
+ if (!isRecord(value)) {
5455
+ return { steps: [], eta: "under a week", confidence: "med" };
5456
+ }
5457
+ const steps = Array.isArray(value.steps) ? value.steps.map(parsePlanStep).filter((step) => step !== null) : [];
5458
+ const eta = typeof value.eta === "string" && value.eta.trim().length > 0 ? value.eta : "under a week";
5459
+ return {
5460
+ steps,
5461
+ eta,
5462
+ confidence: parseConfidence(value.confidence)
5463
+ };
5464
+ }
5465
+ function parseIntent(value) {
5466
+ if (!isRecord(value)) return null;
5467
+ const id = typeof value.id === "string" ? value.id : null;
5468
+ const workspaceId = typeof value.workspace_id === "string" ? value.workspace_id : null;
5469
+ const slug = typeof value.slug === "string" ? value.slug : null;
5470
+ const text2 = typeof value.text === "string" ? value.text : null;
5471
+ const status = parseStatus(value.status);
5472
+ const createdAt = typeof value.created_at === "string" ? value.created_at : null;
5473
+ const updatedAt = typeof value.updated_at === "string" ? value.updated_at : null;
5474
+ if (!id || !workspaceId || !slug || !text2 || !status || !createdAt || !updatedAt) {
5475
+ return null;
5476
+ }
5477
+ return {
5478
+ id,
5479
+ workspace_id: workspaceId,
5480
+ slug,
5481
+ text: text2,
5482
+ status,
5483
+ suggested_agent_slug: parseAgentSlug(value.suggested_agent_slug),
5484
+ plan_preview: parsePlanPreview(value.plan_preview),
5485
+ approved_at: typeof value.approved_at === "string" ? value.approved_at : null,
5486
+ created_at: createdAt,
5487
+ updated_at: updatedAt
5488
+ };
5489
+ }
5490
+ async function parseResponseBody4(response) {
5491
+ const text2 = await response.text();
5492
+ if (!text2) return null;
5493
+ try {
5494
+ return JSON.parse(text2);
5495
+ } catch {
5496
+ return text2;
5497
+ }
5498
+ }
5499
+ function formatHttpError3(status, body) {
5500
+ if (typeof body === "string" && body.trim().length > 0) {
5501
+ return `HTTP ${status}: ${body}`;
5502
+ }
5503
+ if (isRecord(body)) {
5504
+ if (isRecord(body.error)) {
5505
+ const message = typeof body.error.message === "string" ? body.error.message : null;
5506
+ const code = typeof body.error.code === "string" ? body.error.code : null;
5507
+ if (message) return `HTTP ${status} (${code ?? "error"}): ${message}`;
5508
+ }
5509
+ if (typeof body.error === "string" && body.error.trim().length > 0) {
5510
+ return `HTTP ${status}: ${body.error}`;
5511
+ }
5512
+ }
5513
+ return `HTTP ${status}`;
5514
+ }
5515
+ async function requireOrgxAuth3(options = {}) {
5516
+ const auth = await resolveOrgxAuth(options);
5517
+ if (!auth) {
5518
+ throw new Error(
5519
+ "No OrgX API key configured. Run `orgx-wizard auth login` or `orgx-wizard auth set-key <oxk_...>` first."
5520
+ );
5521
+ }
5522
+ return auth;
5523
+ }
5524
+ async function createIntent(input, options = {}) {
5525
+ const text2 = input.text.trim();
5526
+ if (!text2) {
5527
+ throw new Error("Intent text is required.");
5528
+ }
5529
+ if (text2.length > 4e3) {
5530
+ throw new Error("Intent text exceeds 4,000 characters.");
5531
+ }
5532
+ const workspaceId = input.workspace_id.trim();
5533
+ if (!workspaceId) {
5534
+ throw new Error("Workspace id is required.");
5535
+ }
5536
+ const auth = await requireOrgxAuth3(options);
5537
+ const url = buildOrgxApiUrl("/v1/intents", auth.baseUrl);
5538
+ const response = await fetch(url, {
5539
+ method: "POST",
5540
+ headers: {
5541
+ Authorization: `Bearer ${auth.apiKey}`,
5542
+ "Content-Type": "application/json"
5543
+ },
5544
+ body: JSON.stringify({ workspace_id: workspaceId, text: text2 }),
5545
+ signal: AbortSignal.timeout(15e3)
5546
+ });
5547
+ const body = await parseResponseBody4(response);
5548
+ if (!response.ok) {
5549
+ throw new Error(
5550
+ `Failed to create intent. ${formatHttpError3(response.status, body)}`
5551
+ );
5552
+ }
5553
+ const intent = parseIntent(body);
5554
+ if (!intent) {
5555
+ throw new Error("OrgX returned an unexpected intent payload.");
5556
+ }
5557
+ return intent;
5558
+ }
5559
+ async function updateIntent(id, input, options = {}) {
5560
+ const trimmedId = id.trim();
5561
+ if (!trimmedId) {
5562
+ throw new Error("Intent id is required.");
5563
+ }
5564
+ const auth = await requireOrgxAuth3(options);
5565
+ const url = buildOrgxApiUrl(`/v1/intents/${encodeURIComponent(trimmedId)}`, auth.baseUrl);
5566
+ const response = await fetch(url, {
5567
+ method: "PATCH",
5568
+ headers: {
5569
+ Authorization: `Bearer ${auth.apiKey}`,
5570
+ "Content-Type": "application/json"
5571
+ },
5572
+ body: JSON.stringify(input),
5573
+ signal: AbortSignal.timeout(15e3)
5574
+ });
5575
+ const body = await parseResponseBody4(response);
5576
+ if (!response.ok) {
5577
+ throw new Error(
5578
+ `Failed to update intent. ${formatHttpError3(response.status, body)}`
5579
+ );
5580
+ }
5581
+ const intent = parseIntent(body);
5582
+ if (!intent) {
5583
+ throw new Error("OrgX returned an unexpected intent update payload.");
5584
+ }
5585
+ return intent;
5586
+ }
5587
+ function buildIntentHandoffUrl(slug, options = {}) {
5588
+ const base = options.baseUrl?.trim() || "https://useorgx.com";
5589
+ const normalized = base.replace(/\/+$/, "");
5590
+ return `${normalized}/command?intent=${encodeURIComponent(slug)}`;
5591
+ }
5592
+
5593
+ // src/lib/intent-render.ts
5594
+ function detectRenderCapabilities(env = process.env) {
5595
+ const noColor = Boolean(env.NO_COLOR && env.NO_COLOR.length > 0);
5596
+ const asciiOnly = (env.LC_ALL ?? "").toUpperCase() === "C" || (env.LANG ?? "").toUpperCase() === "C";
5597
+ return { noColor, asciiOnly };
5598
+ }
5599
+ function isInteractive(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
5600
+ if (!isTTY) return false;
5601
+ if (env.CI === "1" || env.CI === "true") return false;
5602
+ if (env.GITHUB_ACTIONS === "true") return false;
5603
+ return true;
5604
+ }
5605
+ var CSI = "\x1B[";
5606
+ function wrap(code, text2, enabled) {
5607
+ if (!enabled) return text2;
5608
+ return `${CSI}${code}m${text2}${CSI}0m`;
5609
+ }
5610
+ function colorize(text2, ansi256, options = {}) {
5611
+ return wrap(`38;5;${ansi256}`, text2, !options.noColor);
5612
+ }
5613
+ function bold(text2, options = {}) {
5614
+ return wrap("1", text2, !options.noColor);
5615
+ }
5616
+ function dim(text2, options = {}) {
5617
+ return wrap("2", text2, !options.noColor);
5618
+ }
5619
+ function formatConfidence(value) {
5620
+ const bounded = Math.max(0, Math.min(1, value));
5621
+ return bounded.toFixed(2);
5622
+ }
5623
+ function renderRoutingLine(agent, confidence, options = {}) {
5624
+ const arrow = options.asciiOnly ? "->" : "\u2192";
5625
+ const separator = options.asciiOnly ? " - " : " \xB7 ";
5626
+ const role = AGENT_ROLES[agent];
5627
+ const name = agent.charAt(0).toUpperCase() + agent.slice(1);
5628
+ const coloredName = colorize(name, AGENT_ANSI_256[agent], options);
5629
+ const confidenceLabel = dim("confidence", options);
5630
+ const confidenceValue = formatConfidence(confidence);
5631
+ return ` ${arrow} ${bold(coloredName, options)}${separator}${role} ${confidenceLabel} ${confidenceValue}`;
5632
+ }
5633
+ function resolveConfidenceDecimal(plan) {
5634
+ return CONFIDENCE_DECIMAL[plan.confidence];
5635
+ }
5636
+ function renderPlanPreview(plan, options = {}) {
5637
+ const horizontal = options.asciiOnly ? "-" : "\u2500";
5638
+ const topLeft = options.asciiOnly ? "+" : "\u250C";
5639
+ const topRight = options.asciiOnly ? "+" : "\u2510";
5640
+ const bottomLeft = options.asciiOnly ? "+" : "\u2514";
5641
+ const bottomRight = options.asciiOnly ? "+" : "\u2518";
5642
+ const vertical = options.asciiOnly ? "|" : "\u2502";
5643
+ const steps = plan.steps.map((step, index) => {
5644
+ const number = `${index + 1}.`.padEnd(3, " ");
5645
+ return `${number} ${step.title}`;
5646
+ });
5647
+ const etaLine = `ETA ${plan.eta}`;
5648
+ const lines = [...steps, "", etaLine];
5649
+ const label = "Plan preview";
5650
+ const contentWidth = Math.max(
5651
+ // Keep enough room for the top-chrome label plus its "─ " prefix and
5652
+ // a minimum trailing dash so the border visually wraps the label.
5653
+ label.length + 4,
5654
+ ...lines.map((line) => line.length)
5655
+ );
5656
+ const innerWidth = contentWidth + 2;
5657
+ const leadingDashes = horizontal.repeat(1);
5658
+ const trailingDashes = horizontal.repeat(
5659
+ Math.max(1, innerWidth - label.length - 3)
5660
+ // leading dash + 2 spaces
5661
+ );
5662
+ const top = `${topLeft}${leadingDashes} ${label} ${trailingDashes}${topRight}`;
5663
+ const body = lines.map((line) => {
5664
+ const padded = line.padEnd(contentWidth, " ");
5665
+ return `${vertical} ${padded} ${vertical}`;
5666
+ }).join("\n");
5667
+ const bottom = `${bottomLeft}${horizontal.repeat(innerWidth)}${bottomRight}`;
5668
+ return [top, body, bottom].join("\n");
5669
+ }
5670
+ var TONE_ANSI = {
5671
+ primary: 190,
5672
+ // lime (--ox-primary)
5673
+ warning: 214,
5674
+ // amber (adjust)
5675
+ danger: 203,
5676
+ // rose (archive)
5677
+ neutral: 244
5678
+ // bright black (quit)
5679
+ };
5680
+ function renderHotkeyBar(entries, options = {}) {
5681
+ const prefix = options.asciiOnly ? ">>" : ">>";
5682
+ const segments = entries.map((entry) => {
5683
+ const tone = entry.tone ?? "neutral";
5684
+ const keyLabel = `[${entry.key.toUpperCase()}]`;
5685
+ const coloredKey = colorize(keyLabel, TONE_ANSI[tone], options);
5686
+ return `${coloredKey}${entry.label}`;
5687
+ });
5688
+ return `${prefix} ${segments.join(" ")}`;
5689
+ }
5690
+ var DEFAULT_HOTKEYS = [
5691
+ { key: "a", label: "pprove", tone: "primary" },
5692
+ { key: "e", label: "dit in browser", tone: "warning" },
5693
+ { key: "x", label: "archive", tone: "danger" },
5694
+ { key: "q", label: "uit", tone: "neutral" }
5695
+ ];
5696
+ function normalizeHotkey(input) {
5697
+ const trimmed = input.trim().toLowerCase();
5698
+ if (trimmed.length === 0) return null;
5699
+ const ch = trimmed.charAt(0);
5700
+ switch (ch) {
5701
+ case "a":
5702
+ return "approve";
5703
+ case "e":
5704
+ return "edit";
5705
+ case "x":
5706
+ return "archive";
5707
+ case "q":
5708
+ return "quit";
5709
+ default:
5710
+ return null;
5711
+ }
5712
+ }
5713
+
5406
5714
  // src/lib/daily-brief-onboarding.ts
5715
+ function extractErrorHint(body) {
5716
+ if (!body) return null;
5717
+ let parsed;
5718
+ try {
5719
+ parsed = JSON.parse(body);
5720
+ } catch {
5721
+ return body.slice(0, 160);
5722
+ }
5723
+ if (!parsed || typeof parsed !== "object") return null;
5724
+ const obj = parsed;
5725
+ const err = obj.error;
5726
+ if (typeof err === "string") return err;
5727
+ if (err && typeof err === "object") {
5728
+ const message = err.message;
5729
+ if (typeof message === "string") return message;
5730
+ const code = err.code;
5731
+ if (typeof code === "string") return code;
5732
+ }
5733
+ return null;
5734
+ }
5407
5735
  var BASELINE_PROMPTS = [
5408
5736
  { task_type: "code_review", label: "Code review", placeholder: "20" },
5409
5737
  { task_type: "prd_draft", label: "PRD draft", placeholder: "45" },
@@ -5563,9 +5891,10 @@ async function runDailyBriefOnboarding(options) {
5563
5891
  });
5564
5892
  if (!commitResponse.ok) {
5565
5893
  const text2 = await commitResponse.text().catch(() => "");
5894
+ const hint = extractErrorHint(text2) ?? `HTTP ${commitResponse.status}`;
5566
5895
  return {
5567
5896
  status: "failed",
5568
- message: "Could not commit onboarding capture.",
5897
+ message: `Could not commit onboarding capture \u2014 ${hint}`,
5569
5898
  error: `HTTP ${commitResponse.status}: ${text2.slice(0, 200)}`
5570
5899
  };
5571
5900
  }
@@ -5596,536 +5925,6 @@ async function fetchOnboardingState(auth) {
5596
5925
  }
5597
5926
  }
5598
5927
 
5599
- // src/lib/people-first-capture.ts
5600
- var RELATIONSHIP_STAGE_OPTIONS = [
5601
- { value: "stranger", label: "Stranger", hint: "no prior contact yet" },
5602
- { value: "prospect", label: "Prospect", hint: "outreach planned or in flight (default)" },
5603
- { value: "conversation", label: "Conversation", hint: "active 2-way dialogue" },
5604
- { value: "design_partner", label: "Design partner", hint: "paid / equity track underway" },
5605
- { value: "paused", label: "Paused", hint: "intentional pause, not wrong fit" },
5606
- { value: "churned", label: "Churned", hint: "ended; history preserved" },
5607
- { value: "alumni", label: "Alumni", hint: "closed engagement, may return" }
5608
- ];
5609
- var TRUST_TIER_OPTIONS = [
5610
- { value: "cold", label: "Cold / unknown", hint: "new or barely-met" },
5611
- { value: "warm", label: "Warm", hint: "know each other, some trust built" },
5612
- { value: "close", label: "Close", hint: "deep trust \u2014 inner-circle candidate" }
5613
- ];
5614
- async function fetchPeopleFirstOnboardingState(auth) {
5615
- try {
5616
- const res = await fetch(
5617
- buildOrgxApiUrl("/v1/people/onboarding", auth.baseUrl),
5618
- {
5619
- method: "GET",
5620
- headers: { Authorization: `Bearer ${auth.apiKey}` },
5621
- signal: AbortSignal.timeout(5e3)
5622
- }
5623
- );
5624
- if (!res.ok) return null;
5625
- const body = await res.json().catch(() => null);
5626
- if (!body || !Array.isArray(body.workspaces)) return null;
5627
- return body;
5628
- } catch {
5629
- return null;
5630
- }
5631
- }
5632
- function parseContactChannels(raw) {
5633
- const channels = [];
5634
- const parts = raw.split(/[,\n]+/).map((part) => part.trim()).filter(Boolean);
5635
- for (const part of parts) {
5636
- if (/^https?:\/\/(?:www\.)?linkedin\.com\//i.test(part) || part.toLowerCase().startsWith("linkedin.com/")) {
5637
- channels.push({ kind: "linkedin", value: part });
5638
- } else if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(part)) {
5639
- channels.push({ kind: "email", value: part });
5640
- } else if (/^\+?[\d][\d\s()\-.]{5,}$/.test(part)) {
5641
- channels.push({ kind: "phone", value: part.replace(/\s+/g, "") });
5642
- } else {
5643
- channels.push({ kind: "linkedin", value: part });
5644
- }
5645
- }
5646
- return channels;
5647
- }
5648
- async function postJson(auth, path, body) {
5649
- try {
5650
- const res = await fetch(buildOrgxApiUrl(path, auth.baseUrl), {
5651
- method: "POST",
5652
- headers: {
5653
- Authorization: `Bearer ${auth.apiKey}`,
5654
- "Content-Type": "application/json"
5655
- },
5656
- body: JSON.stringify(body),
5657
- signal: AbortSignal.timeout(1e4)
5658
- });
5659
- const text2 = await res.text().catch(() => "");
5660
- let parsed = null;
5661
- if (text2) {
5662
- try {
5663
- parsed = JSON.parse(text2);
5664
- } catch {
5665
- parsed = text2;
5666
- }
5667
- }
5668
- if (!res.ok) {
5669
- const detail = parsed && typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `HTTP ${res.status}`;
5670
- return { ok: false, error: detail };
5671
- }
5672
- return { ok: true, status: res.status, body: parsed };
5673
- } catch (err) {
5674
- const message = err instanceof Error ? err.message : String(err);
5675
- return { ok: false, error: message };
5676
- }
5677
- }
5678
- function extractId(body) {
5679
- if (body && typeof body === "object") {
5680
- const candidate = body.id ?? body.data;
5681
- if (typeof candidate === "string") return candidate;
5682
- if (candidate && typeof candidate === "object") {
5683
- const innerId = candidate.id;
5684
- if (typeof innerId === "string") return innerId;
5685
- }
5686
- }
5687
- return null;
5688
- }
5689
- var BACKEND_UNREACHABLE_HINT = "Could not sync with OrgX backend \u2014 check your network / run `orgx-wizard status`.";
5690
- async function runPeopleFirstCapture(options) {
5691
- if (!options.interactive) {
5692
- return {
5693
- status: "skipped_non_interactive",
5694
- message: "People-first capture skipped \u2014 not attached to a TTY."
5695
- };
5696
- }
5697
- if (!options.workspace) {
5698
- return {
5699
- status: "skipped_no_workspace",
5700
- message: "People-first capture skipped \u2014 no workspace resolved."
5701
- };
5702
- }
5703
- if (options.alreadyCompleted) {
5704
- return {
5705
- status: "skipped_already_completed",
5706
- message: "People-first capture already completed for this workspace."
5707
- };
5708
- }
5709
- const auth = await resolveOrgxAuth();
5710
- if (!auth) {
5711
- return {
5712
- status: "failed",
5713
- message: "People-first capture needs OrgX auth.",
5714
- error: "no_auth"
5715
- };
5716
- }
5717
- const state = await fetchPeopleFirstOnboardingState(auth);
5718
- if (state) {
5719
- const row = state.workspaces.find((w) => w.id === options.workspace.id);
5720
- if (row?.peopleFirstCaptureCompletedAt) {
5721
- return {
5722
- status: "skipped_already_completed",
5723
- message: "People-first capture already completed for this workspace."
5724
- };
5725
- }
5726
- }
5727
- const { prompts } = options;
5728
- const proceed = await prompts.confirm({
5729
- message: "Name the first person OrgX should know about? (takes ~30 seconds; OrgX will pre-draft a first-touch artifact for them)",
5730
- initialValue: true
5731
- });
5732
- if (prompts.isCancel(proceed)) {
5733
- return { status: "cancelled", message: "People-first capture cancelled." };
5734
- }
5735
- if (!proceed) {
5736
- return {
5737
- status: "skipped_declined",
5738
- message: "Skipped \u2014 run the wizard again any time to add your first person."
5739
- };
5740
- }
5741
- const headlineAnswer = await prompts.text({
5742
- message: 'Who is this person to you? (one line \u2014 e.g. "prospect at Acme", "design partner", "old teammate")',
5743
- placeholder: "design partner prospect for OrgX",
5744
- validate(value) {
5745
- if (!value || !value.trim()) return "Enter one line.";
5746
- if (value.trim().length > 160) return "Keep it under 160 chars.";
5747
- return void 0;
5748
- }
5749
- });
5750
- if (prompts.isCancel(headlineAnswer)) {
5751
- return { status: "cancelled", message: "People-first capture cancelled." };
5752
- }
5753
- const headline = typeof headlineAnswer === "string" ? headlineAnswer.trim() : "";
5754
- const contextAnswer = await prompts.select({
5755
- initialValue: "personal",
5756
- message: "Is this on behalf of a business, or a personal relationship?",
5757
- options: [
5758
- { value: "personal", label: "Personal", hint: "no business attached yet" },
5759
- { value: "business", label: "On behalf of a business", hint: "will also create a Business entity" }
5760
- ]
5761
- });
5762
- if (prompts.isCancel(contextAnswer)) {
5763
- return { status: "cancelled", message: "People-first capture cancelled." };
5764
- }
5765
- const hasBusiness = contextAnswer === "business";
5766
- let businessName;
5767
- if (hasBusiness) {
5768
- const businessAnswer = await prompts.text({
5769
- message: "Business name?",
5770
- placeholder: "Acme Treasury",
5771
- validate(value) {
5772
- if (!value || !value.trim()) return "Enter the business name.";
5773
- return void 0;
5774
- }
5775
- });
5776
- if (prompts.isCancel(businessAnswer)) {
5777
- return { status: "cancelled", message: "People-first capture cancelled." };
5778
- }
5779
- businessName = typeof businessAnswer === "string" ? businessAnswer.trim() : void 0;
5780
- }
5781
- const nameAnswer = await prompts.text({
5782
- message: "Their name?",
5783
- placeholder: "Laura Chen",
5784
- validate(value) {
5785
- if (!value || !value.trim()) return "Enter a display name.";
5786
- return void 0;
5787
- }
5788
- });
5789
- if (prompts.isCancel(nameAnswer)) {
5790
- return { status: "cancelled", message: "People-first capture cancelled." };
5791
- }
5792
- const displayName = typeof nameAnswer === "string" ? nameAnswer.trim() : "";
5793
- const contactAnswer = await prompts.text({
5794
- message: "How do you reach them? (email, LinkedIn URL, or phone \u2014 comma-separated, at least one)",
5795
- placeholder: "laura@acme.com, linkedin.com/in/laurachen",
5796
- validate(value) {
5797
- if (!value || !value.trim()) return "Enter at least one contact.";
5798
- const parsed = parseContactChannels(value);
5799
- if (parsed.length === 0) return "Could not parse any contact channels.";
5800
- return void 0;
5801
- }
5802
- });
5803
- if (prompts.isCancel(contactAnswer)) {
5804
- return { status: "cancelled", message: "People-first capture cancelled." };
5805
- }
5806
- const contactChannels = parseContactChannels(
5807
- typeof contactAnswer === "string" ? contactAnswer : ""
5808
- );
5809
- const stageAnswer = await prompts.select({
5810
- initialValue: "prospect",
5811
- message: "What stage is the relationship?",
5812
- options: RELATIONSHIP_STAGE_OPTIONS.map((opt) => ({
5813
- value: opt.value,
5814
- label: opt.label,
5815
- ...opt.hint ? { hint: opt.hint } : {}
5816
- }))
5817
- });
5818
- if (prompts.isCancel(stageAnswer)) {
5819
- return { status: "cancelled", message: "People-first capture cancelled." };
5820
- }
5821
- const relationshipStage = stageAnswer;
5822
- const trustAnswer = await prompts.select({
5823
- initialValue: "cold",
5824
- message: "How would you describe the trust level right now?",
5825
- options: TRUST_TIER_OPTIONS.map((opt) => ({
5826
- value: opt.value,
5827
- label: opt.label,
5828
- ...opt.hint ? { hint: opt.hint } : {}
5829
- }))
5830
- });
5831
- if (prompts.isCancel(trustAnswer)) {
5832
- return { status: "cancelled", message: "People-first capture cancelled." };
5833
- }
5834
- const trustTier = trustAnswer;
5835
- const innerCircleAnswer = await prompts.confirm({
5836
- message: "Mark this person as inner-circle? (affects tone of drafted artifacts)",
5837
- initialValue: false
5838
- });
5839
- if (prompts.isCancel(innerCircleAnswer)) {
5840
- return { status: "cancelled", message: "People-first capture cancelled." };
5841
- }
5842
- const innerCircle = Boolean(innerCircleAnswer);
5843
- const goalAnswer = await prompts.text({
5844
- message: "Which matters most right now? (one line \u2014 this becomes their Goal)",
5845
- placeholder: "agree on first pilot scope by May 3",
5846
- validate(value) {
5847
- if (!value || !value.trim()) return "Enter one line.";
5848
- return void 0;
5849
- }
5850
- });
5851
- if (prompts.isCancel(goalAnswer)) {
5852
- return { status: "cancelled", message: "People-first capture cancelled." };
5853
- }
5854
- const goalTitle = typeof goalAnswer === "string" ? goalAnswer.trim() : "";
5855
- let businessIntent;
5856
- if (hasBusiness && relationshipStage === "conversation") {
5857
- const intentAnswer = await prompts.select({
5858
- initialValue: "client",
5859
- message: "Is this person an investor in this business, or a client of it?",
5860
- options: [
5861
- { value: "client", label: "Client / prospect", hint: "default" },
5862
- { value: "investor", label: "Investor / advisor" },
5863
- { value: "unspecified", label: "Unsure / neither" }
5864
- ]
5865
- });
5866
- if (prompts.isCancel(intentAnswer)) {
5867
- return { status: "cancelled", message: "People-first capture cancelled." };
5868
- }
5869
- businessIntent = intentAnswer;
5870
- }
5871
- let businessId;
5872
- if (hasBusiness && businessName) {
5873
- const businessRes = await postJson(auth, "/v1/businesses", {
5874
- display_name: businessName,
5875
- relationship_stage: relationshipStage === "alumni" ? "alumni" : "engaged"
5876
- });
5877
- if (!businessRes.ok) {
5878
- return {
5879
- status: "failed",
5880
- message: `${BACKEND_UNREACHABLE_HINT} (businesses: ${businessRes.error})`,
5881
- error: businessRes.error
5882
- };
5883
- }
5884
- businessId = extractId(businessRes.body) ?? void 0;
5885
- }
5886
- const personPayload = {
5887
- display_name: displayName,
5888
- headline,
5889
- relationship_stage: relationshipStage,
5890
- contact_channels: contactChannels,
5891
- metadata: {
5892
- captured_via: "orgx-wizard",
5893
- inner_circle: innerCircle,
5894
- trust_tier: trustTier,
5895
- ...businessIntent ? { business_intent: businessIntent } : {}
5896
- },
5897
- ...businessId ? { business_id: businessId } : {}
5898
- };
5899
- const personRes = await postJson(auth, "/v1/people", personPayload);
5900
- if (!personRes.ok) {
5901
- return {
5902
- status: "failed",
5903
- message: `${BACKEND_UNREACHABLE_HINT} (people: ${personRes.error})`,
5904
- error: personRes.error
5905
- };
5906
- }
5907
- const personId = extractId(personRes.body) ?? void 0;
5908
- if (!personId) {
5909
- return {
5910
- status: "failed",
5911
- message: `${BACKEND_UNREACHABLE_HINT} (people: missing id in response)`,
5912
- error: "missing_person_id"
5913
- };
5914
- }
5915
- const goalRes = await postJson(auth, "/v1/goals", {
5916
- owner_type: "person",
5917
- owner_id: personId,
5918
- title: goalTitle
5919
- });
5920
- if (!goalRes.ok) {
5921
- return {
5922
- status: "failed",
5923
- message: `${BACKEND_UNREACHABLE_HINT} (goals: ${goalRes.error})`,
5924
- error: goalRes.error
5925
- };
5926
- }
5927
- const goalId = extractId(goalRes.body) ?? void 0;
5928
- if (!goalId) {
5929
- return {
5930
- status: "failed",
5931
- message: `${BACKEND_UNREACHABLE_HINT} (goals: missing id in response)`,
5932
- error: "missing_goal_id"
5933
- };
5934
- }
5935
- const person = {
5936
- id: personId,
5937
- display_name: displayName,
5938
- headline,
5939
- relationship_stage: relationshipStage,
5940
- contact_channels: contactChannels,
5941
- inner_circle: innerCircle,
5942
- trust_tier: trustTier,
5943
- ...businessId ? { business_id: businessId } : {},
5944
- ...businessIntent ? { business_intent: businessIntent } : {}
5945
- };
5946
- const goal = {
5947
- id: goalId,
5948
- owner_type: "person",
5949
- owner_id: personId,
5950
- title: goalTitle
5951
- };
5952
- return {
5953
- status: "completed",
5954
- message: `Captured ${displayName} in your workspace.`,
5955
- person,
5956
- goal
5957
- };
5958
- }
5959
-
5960
- // src/peopleFirst/templateSelect.ts
5961
- function selectPeopleFirstTemplate(input) {
5962
- const trustTier = input.trustTier ?? "cold";
5963
- if (input.innerCircle) {
5964
- return {
5965
- persona: "inner_circle",
5966
- reason: "inner-circle mark set by user",
5967
- needsBusinessIntentPrompt: false
5968
- };
5969
- }
5970
- switch (input.relationshipStage) {
5971
- case "stranger":
5972
- case "prospect":
5973
- return {
5974
- persona: "cold_outreach",
5975
- reason: `stage=${input.relationshipStage} \u2014 cold outreach draft`,
5976
- needsBusinessIntentPrompt: false
5977
- };
5978
- case "conversation": {
5979
- if (input.hasBusiness) {
5980
- if (input.businessIntent === "investor") {
5981
- return {
5982
- persona: "investor_prep",
5983
- reason: "conversation + business + investor intent",
5984
- needsBusinessIntentPrompt: false
5985
- };
5986
- }
5987
- if (input.businessIntent === "client") {
5988
- return {
5989
- persona: "client_trust",
5990
- reason: "conversation + business + client intent",
5991
- needsBusinessIntentPrompt: false
5992
- };
5993
- }
5994
- return {
5995
- persona: "client_trust",
5996
- reason: "conversation + business \u2014 defaulting to client_trust; ask to confirm",
5997
- needsBusinessIntentPrompt: true
5998
- };
5999
- }
6000
- if (trustTier === "warm" || trustTier === "close") {
6001
- return {
6002
- persona: "founder_ally",
6003
- reason: `conversation + trust=${trustTier} \u2014 founder-ally outreach`,
6004
- needsBusinessIntentPrompt: false
6005
- };
6006
- }
6007
- return {
6008
- persona: "cold_outreach",
6009
- reason: "conversation + cold trust \u2014 treating as cold_outreach",
6010
- needsBusinessIntentPrompt: false
6011
- };
6012
- }
6013
- case "design_partner":
6014
- case "active_client":
6015
- return {
6016
- persona: "client_trust",
6017
- reason: `stage=${input.relationshipStage} \u2014 client_trust cadence`,
6018
- needsBusinessIntentPrompt: false
6019
- };
6020
- case "alumni":
6021
- return {
6022
- persona: "alumni_touch",
6023
- reason: "stage=alumni \u2014 alumni_touch rekindle",
6024
- needsBusinessIntentPrompt: false
6025
- };
6026
- case "paused":
6027
- case "churned":
6028
- return {
6029
- persona: "alumni_touch",
6030
- reason: `stage=${input.relationshipStage} \u2014 treating as alumni_touch for rekindle tone`,
6031
- needsBusinessIntentPrompt: false
6032
- };
6033
- }
6034
- }
6035
-
6036
- // src/lib/people-first-artifact.ts
6037
- async function callDraftEndpoint(auth, body) {
6038
- try {
6039
- const res = await fetch(buildOrgxApiUrl("/v1/artifacts/draft", auth.baseUrl), {
6040
- method: "POST",
6041
- headers: {
6042
- Authorization: `Bearer ${auth.apiKey}`,
6043
- "Content-Type": "application/json"
6044
- },
6045
- body: JSON.stringify(body),
6046
- signal: AbortSignal.timeout(1e4)
6047
- });
6048
- if (!res.ok) {
6049
- return { ok: false, error: `HTTP ${res.status}` };
6050
- }
6051
- const data = await res.json().catch(() => null);
6052
- return { ok: true, data: data ?? {} };
6053
- } catch (err) {
6054
- const message = err instanceof Error ? err.message : String(err);
6055
- return { ok: false, error: message };
6056
- }
6057
- }
6058
- async function runPeopleFirstArtifactDraft(options) {
6059
- if (!options.interactive) {
6060
- return {
6061
- status: "skipped_non_interactive",
6062
- message: "Artifact draft skipped \u2014 not attached to a TTY."
6063
- };
6064
- }
6065
- const auth = await resolveOrgxAuth();
6066
- if (!auth) {
6067
- return {
6068
- status: "failed",
6069
- message: "Artifact draft needs OrgX auth.",
6070
- error: "no_auth"
6071
- };
6072
- }
6073
- let businessIntent = options.businessIntent;
6074
- const { prompts, person } = options;
6075
- const hasBusiness = Boolean(options.businessName || person.business_id);
6076
- const preselection = selectPeopleFirstTemplate({
6077
- relationshipStage: person.relationship_stage,
6078
- hasBusiness,
6079
- trustTier: person.trust_tier,
6080
- innerCircle: person.inner_circle,
6081
- ...businessIntent ? { businessIntent } : {}
6082
- });
6083
- let persona = preselection.persona;
6084
- if (preselection.needsBusinessIntentPrompt && !businessIntent) {
6085
- const answer = await prompts.select({
6086
- initialValue: "client",
6087
- message: "Is this more of a client-trust update, or investor-prep?",
6088
- options: [
6089
- { value: "client", label: "Client trust (default)" },
6090
- { value: "investor", label: "Investor prep" }
6091
- ]
6092
- });
6093
- if (prompts.isCancel(answer)) {
6094
- return { status: "cancelled", message: "Artifact draft cancelled." };
6095
- }
6096
- businessIntent = answer;
6097
- persona = selectPeopleFirstTemplate({
6098
- relationshipStage: person.relationship_stage,
6099
- hasBusiness,
6100
- trustTier: person.trust_tier,
6101
- innerCircle: person.inner_circle,
6102
- businessIntent
6103
- }).persona;
6104
- }
6105
- const serverResult = await callDraftEndpoint(auth, {
6106
- template_persona: persona,
6107
- person_id: person.id,
6108
- calibration_inputs: {
6109
- voice_examples: []
6110
- }
6111
- });
6112
- if (!serverResult.ok) {
6113
- return {
6114
- status: "failed",
6115
- message: `Could not draft artifact \u2014 ${serverResult.error}. Check your network / run \`orgx-wizard status\`.`,
6116
- persona,
6117
- error: serverResult.error
6118
- };
6119
- }
6120
- return {
6121
- status: "drafted",
6122
- message: `Drafted ${persona} artifact for ${person.display_name}.`,
6123
- persona,
6124
- ...serverResult.data.artifact_id ? { serverArtifactId: serverResult.data.artifact_id } : {},
6125
- ...serverResult.data.url ? { serverArtifactUrl: serverResult.data.url } : {}
6126
- };
6127
- }
6128
-
6129
5928
  // src/spinner.ts
6130
5929
  import ora from "ora";
6131
5930
  import pc2 from "picocolors";
@@ -6520,6 +6319,296 @@ function parseTimeoutSeconds(value) {
6520
6319
  }
6521
6320
  return parsed;
6522
6321
  }
6322
+ async function readSingleKey() {
6323
+ const stdin = process.stdin;
6324
+ if (!stdin.isTTY) return null;
6325
+ const previousRawMode = stdin.isRaw === true;
6326
+ return await new Promise((resolve) => {
6327
+ const cleanup = (result) => {
6328
+ stdin.off("data", onData);
6329
+ if (stdin.isTTY) {
6330
+ stdin.setRawMode(previousRawMode);
6331
+ }
6332
+ stdin.pause();
6333
+ resolve(result);
6334
+ };
6335
+ const onData = (chunk) => {
6336
+ const text2 = chunk.toString("utf8");
6337
+ const first = text2.charAt(0);
6338
+ if (first === "" || first === "") {
6339
+ cleanup(null);
6340
+ return;
6341
+ }
6342
+ cleanup(first.toLowerCase());
6343
+ };
6344
+ if (stdin.isTTY) {
6345
+ stdin.setRawMode(true);
6346
+ }
6347
+ stdin.resume();
6348
+ stdin.on("data", onData);
6349
+ });
6350
+ }
6351
+ function printIntentPreview(intent, baseUrl) {
6352
+ const render = detectRenderCapabilities();
6353
+ const agent = intent.suggested_agent_slug ?? "xandy";
6354
+ const confidence = resolveConfidenceDecimal(intent.plan_preview);
6355
+ console.log("");
6356
+ console.log(renderRoutingLine(agent, confidence, render));
6357
+ console.log("");
6358
+ console.log(renderPlanPreview(intent.plan_preview, render));
6359
+ console.log("");
6360
+ console.log(
6361
+ dim(
6362
+ ` slug ${intent.slug} handoff ${buildIntentHandoffUrl(intent.slug, { baseUrl })}`,
6363
+ render
6364
+ )
6365
+ );
6366
+ console.log("");
6367
+ console.log(renderHotkeyBar(DEFAULT_HOTKEYS, render));
6368
+ }
6369
+ async function maybeCaptureSetupIntent(input) {
6370
+ if (!input.interactive || !input.workspace) {
6371
+ return "skipped";
6372
+ }
6373
+ const prompt = await textPrompt({
6374
+ message: "In one line, what do you want OrgX to move first? (blank to skip \u2014 you can start from the dashboard)",
6375
+ placeholder: "e.g. open a warm-intro loop to 50 design-led SaaS founders",
6376
+ validate: (value) => {
6377
+ const trimmed = (value ?? "").trim();
6378
+ if (!trimmed) return void 0;
6379
+ if (trimmed.length > 4e3) return "Keep it under 4,000 characters.";
6380
+ return void 0;
6381
+ }
6382
+ });
6383
+ if (clack.isCancel(prompt)) {
6384
+ return "cancelled";
6385
+ }
6386
+ const intentText = typeof prompt === "string" ? prompt.trim() : "";
6387
+ if (!intentText) {
6388
+ return "skipped";
6389
+ }
6390
+ const spinner = createOrgxSpinner("Routing");
6391
+ spinner.start();
6392
+ let intent;
6393
+ try {
6394
+ intent = await createIntent({
6395
+ workspace_id: input.workspace.id,
6396
+ text: intentText
6397
+ });
6398
+ spinner.stop();
6399
+ } catch (error) {
6400
+ spinner.fail("Could not classify intent.");
6401
+ const message = error instanceof Error ? error.message : String(error);
6402
+ console.log(` ${ICON.warn} ${pc3.yellow("intent")} ${pc3.dim(message)}`);
6403
+ return "failed";
6404
+ }
6405
+ const auth = await resolveOrgxAuth().catch(() => null);
6406
+ const baseUrl = auth?.baseUrl ?? DEFAULT_ORGX_BASE_URL;
6407
+ printIntentPreview(intent, baseUrl);
6408
+ const approveChoice = await clack.confirm({
6409
+ message: "Approve this plan and hand off to OrgX?",
6410
+ initialValue: true
6411
+ });
6412
+ if (clack.isCancel(approveChoice)) {
6413
+ return "cancelled";
6414
+ }
6415
+ if (approveChoice) {
6416
+ const approveSpinner = createOrgxSpinner("Approving");
6417
+ approveSpinner.start();
6418
+ try {
6419
+ await updateIntent(intent.id, { status: "approved" });
6420
+ approveSpinner.stop();
6421
+ } catch (error) {
6422
+ approveSpinner.fail("Approve failed.");
6423
+ const message = error instanceof Error ? error.message : String(error);
6424
+ console.log(` ${ICON.warn} ${pc3.yellow("intent")} ${pc3.dim(message)}`);
6425
+ return "failed";
6426
+ }
6427
+ const handoff2 = buildIntentHandoffUrl(intent.slug, { baseUrl });
6428
+ console.log(` ${ICON.ok} ${pc3.green("intent")} ${pc3.dim(`approved \xB7 ${handoff2}`)}`);
6429
+ return "approved";
6430
+ }
6431
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl });
6432
+ console.log(` ${ICON.skip} ${pc3.dim(`intent pending \xB7 ${handoff}`)}`);
6433
+ return "captured";
6434
+ }
6435
+ async function runIntentCommand(options) {
6436
+ const interactive = isInteractive();
6437
+ let intentText = options.initialText?.trim() ?? "";
6438
+ if (!intentText) {
6439
+ if (!interactive) {
6440
+ console.error(
6441
+ pc3.red("Intent text is required when running non-interactively.")
6442
+ );
6443
+ console.error(
6444
+ pc3.dim(' Example: orgx-wizard intent "launch partner outreach campaign"')
6445
+ );
6446
+ process.exitCode = 1;
6447
+ return;
6448
+ }
6449
+ const prompt = await textPrompt({
6450
+ message: "What do you want to move?",
6451
+ placeholder: "e.g. launch partner outreach campaign",
6452
+ validate: (value) => {
6453
+ const trimmed = (value ?? "").trim();
6454
+ if (!trimmed) return "Intent text cannot be empty.";
6455
+ if (trimmed.length > 4e3) return "Intent text exceeds 4,000 characters.";
6456
+ return void 0;
6457
+ }
6458
+ });
6459
+ if (clack.isCancel(prompt) || typeof prompt !== "string") {
6460
+ console.log(pc3.dim("Cancelled."));
6461
+ return;
6462
+ }
6463
+ intentText = prompt.trim();
6464
+ }
6465
+ let workspaceId = options.workspaceId ?? "";
6466
+ let workspaceName = null;
6467
+ let workspaceBaseUrl = null;
6468
+ if (!workspaceId) {
6469
+ const loadLabel = "Loading current OrgX workspace";
6470
+ const spinner = interactive ? createOrgxSpinner(loadLabel) : null;
6471
+ if (spinner) spinner.start();
6472
+ else console.log(pc3.dim(`${loadLabel}...`));
6473
+ try {
6474
+ const current = await getCurrentWorkspace();
6475
+ if (!current) {
6476
+ if (spinner) spinner.fail("No OrgX workspace is configured.");
6477
+ else console.error(pc3.red("No OrgX workspace is configured."));
6478
+ console.log(
6479
+ pc3.dim(" Run `orgx-wizard workspace create <name>` or pass --workspace <id>.")
6480
+ );
6481
+ process.exitCode = 1;
6482
+ return;
6483
+ }
6484
+ workspaceId = current.id;
6485
+ workspaceName = current.name;
6486
+ if (spinner) spinner.stop();
6487
+ } catch (error) {
6488
+ if (spinner) spinner.fail("Failed to load current workspace.");
6489
+ console.error(
6490
+ pc3.red(error instanceof Error ? error.message : String(error))
6491
+ );
6492
+ process.exitCode = 1;
6493
+ return;
6494
+ }
6495
+ }
6496
+ const classifyLabel = "Routing";
6497
+ const classifySpinner = interactive ? createOrgxSpinner(classifyLabel) : null;
6498
+ if (classifySpinner) classifySpinner.start();
6499
+ else console.log(pc3.dim(`${classifyLabel}...`));
6500
+ let intent;
6501
+ try {
6502
+ intent = await createIntent({ workspace_id: workspaceId, text: intentText });
6503
+ if (classifySpinner) classifySpinner.stop();
6504
+ } catch (error) {
6505
+ if (classifySpinner) classifySpinner.fail("Classification failed.");
6506
+ console.error(
6507
+ pc3.red(error instanceof Error ? error.message : String(error))
6508
+ );
6509
+ process.exitCode = 1;
6510
+ return;
6511
+ }
6512
+ const auth = await resolveOrgxAuth().catch(() => null);
6513
+ const baseUrl = auth?.baseUrl ?? DEFAULT_ORGX_BASE_URL;
6514
+ workspaceBaseUrl = baseUrl;
6515
+ if (options.jsonOutput) {
6516
+ const payload = {
6517
+ intent,
6518
+ handoff_url: buildIntentHandoffUrl(intent.slug, { baseUrl }),
6519
+ workspace: workspaceName ? { id: workspaceId, name: workspaceName } : { id: workspaceId }
6520
+ };
6521
+ console.log(JSON.stringify(payload, null, 2));
6522
+ return;
6523
+ }
6524
+ printIntentPreview(intent, baseUrl);
6525
+ console.log("");
6526
+ if (!interactive) {
6527
+ console.log(
6528
+ pc3.dim(
6529
+ `Run \`orgx-wizard intent --workspace ${workspaceId}\` interactively to approve, edit, or archive.`
6530
+ )
6531
+ );
6532
+ return;
6533
+ }
6534
+ let action = null;
6535
+ while (action === null) {
6536
+ const key = await readSingleKey();
6537
+ if (!key) {
6538
+ action = "quit";
6539
+ break;
6540
+ }
6541
+ action = normalizeHotkey(key);
6542
+ if (action === null) {
6543
+ console.log(
6544
+ pc3.dim(` Unrecognized key. Press a, e, x, or q.`)
6545
+ );
6546
+ }
6547
+ }
6548
+ switch (action) {
6549
+ case "approve": {
6550
+ const approveLabel = "Approving";
6551
+ const spinner = interactive ? createOrgxSpinner(approveLabel) : null;
6552
+ if (spinner) spinner.start();
6553
+ try {
6554
+ await updateIntent(intent.id, { status: "approved" });
6555
+ if (spinner) spinner.stop();
6556
+ } catch (error) {
6557
+ if (spinner) spinner.fail("Approve failed.");
6558
+ console.error(
6559
+ pc3.red(error instanceof Error ? error.message : String(error))
6560
+ );
6561
+ process.exitCode = 1;
6562
+ return;
6563
+ }
6564
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl });
6565
+ console.log("");
6566
+ console.log(`${pc3.green("\u2713")} Approved \xB7 ${handoff}`);
6567
+ return;
6568
+ }
6569
+ case "edit": {
6570
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl });
6571
+ console.log("");
6572
+ console.log(`${pc3.yellow("\u270E")} Opening ${handoff}`);
6573
+ const result = openBrowser(handoff);
6574
+ if (!result.ok) {
6575
+ console.log(pc3.dim(` Could not launch browser: ${result.error}`));
6576
+ console.log(pc3.dim(` Visit ${handoff} to finish editing.`));
6577
+ }
6578
+ return;
6579
+ }
6580
+ case "archive": {
6581
+ const archiveLabel = "Archiving";
6582
+ const spinner = interactive ? createOrgxSpinner(archiveLabel) : null;
6583
+ if (spinner) spinner.start();
6584
+ try {
6585
+ await updateIntent(intent.id, { status: "archived" });
6586
+ if (spinner) spinner.stop();
6587
+ } catch (error) {
6588
+ if (spinner) spinner.fail("Archive failed.");
6589
+ console.error(
6590
+ pc3.red(error instanceof Error ? error.message : String(error))
6591
+ );
6592
+ process.exitCode = 1;
6593
+ return;
6594
+ }
6595
+ console.log("");
6596
+ console.log(`${pc3.red("\u2715")} Archived \xB7 intent will not be executed.`);
6597
+ return;
6598
+ }
6599
+ case "quit":
6600
+ default: {
6601
+ console.log("");
6602
+ console.log(pc3.dim(" Exited without saving. Intent remains pending."));
6603
+ console.log(
6604
+ pc3.dim(
6605
+ ` Resume at ${buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl })}`
6606
+ )
6607
+ );
6608
+ return;
6609
+ }
6610
+ }
6611
+ }
6523
6612
  async function maybeConfigureOptionalWorkspaceAddOns(input) {
6524
6613
  if (!input.interactive || !input.workspace) {
6525
6614
  return "skipped";
@@ -6775,109 +6864,6 @@ async function maybeInstallOptionalCompanionPlugins(input) {
6775
6864
  ...input.telemetry ? { telemetry: input.telemetry } : {}
6776
6865
  });
6777
6866
  }
6778
- async function maybeRunPeopleFirstCapture(input) {
6779
- if (!input.interactive || !input.workspace) {
6780
- return "skipped";
6781
- }
6782
- const alreadyCompleted = hasPeopleFirstCaptureCompleted(input.workspace.id);
6783
- const captureResult = await runPeopleFirstCapture({
6784
- interactive: input.interactive,
6785
- workspace: input.workspace,
6786
- alreadyCompleted,
6787
- prompts: {
6788
- cancel: clack.cancel,
6789
- isCancel: clack.isCancel,
6790
- text: textPrompt,
6791
- select: selectPrompt,
6792
- confirm: clack.confirm
6793
- }
6794
- });
6795
- switch (captureResult.status) {
6796
- case "skipped_already_completed":
6797
- return "skipped";
6798
- case "skipped_non_interactive":
6799
- case "skipped_no_workspace":
6800
- return "skipped";
6801
- case "skipped_declined":
6802
- console.log(` ${ICON.skip} ${pc3.dim(captureResult.message)}`);
6803
- return "skipped";
6804
- case "cancelled":
6805
- return "cancelled";
6806
- case "failed":
6807
- console.log(` ${ICON.warn} ${pc3.yellow("people-first")} ${pc3.dim(captureResult.message)}`);
6808
- return "failed";
6809
- case "completed":
6810
- break;
6811
- }
6812
- if (!captureResult.person || !captureResult.goal) {
6813
- return "failed";
6814
- }
6815
- console.log(
6816
- ` ${ICON.ok} ${pc3.green("people-first")} ${pc3.dim(
6817
- `Captured ${captureResult.person.display_name} (${captureResult.person.relationship_stage}).`
6818
- )}`
6819
- );
6820
- const draftResult = await runPeopleFirstArtifactDraft({
6821
- interactive: input.interactive,
6822
- person: captureResult.person,
6823
- prompts: {
6824
- cancel: clack.cancel,
6825
- isCancel: clack.isCancel,
6826
- text: textPrompt,
6827
- select: selectPrompt,
6828
- confirm: clack.confirm
6829
- }
6830
- });
6831
- if (draftResult.status === "cancelled") {
6832
- } else if (draftResult.status === "failed") {
6833
- console.log(
6834
- ` ${ICON.warn} ${pc3.yellow("artifact draft")} ${pc3.dim(draftResult.message)}`
6835
- );
6836
- } else if (draftResult.status === "drafted") {
6837
- console.log(
6838
- ` ${ICON.ok} ${pc3.green("artifact draft")} ${pc3.dim(
6839
- `OrgX pre-drafted a ${draftResult.persona ?? "first-touch"} artifact.`
6840
- )}`
6841
- );
6842
- }
6843
- try {
6844
- recordPeopleFirstCaptureCompletion({
6845
- workspaceId: input.workspace.id,
6846
- completedAt: (/* @__PURE__ */ new Date()).toISOString(),
6847
- ...captureResult.person ? { personId: captureResult.person.id } : {},
6848
- ...draftResult.persona ? { templatePersona: draftResult.persona } : {}
6849
- });
6850
- } catch {
6851
- }
6852
- const baseUrl = process.env.ORGX_APP_URL?.trim() || DEFAULT_ORGX_BASE_URL;
6853
- const commandUrl = `${baseUrl.replace(/\/+$/, "")}/command`;
6854
- console.log("");
6855
- console.log(
6856
- ` ${ICON.ok} ${pc3.bold(
6857
- `You brought in ${captureResult.person.display_name}.`
6858
- )} ${pc3.dim(
6859
- `Head to ${commandUrl} to see them in your people list \u2014 OrgX already drafted a first-touch artifact.`
6860
- )}`
6861
- );
6862
- if (input.openInBrowser) {
6863
- const openResult = openBrowser(commandUrl);
6864
- if (!openResult.ok && openResult.error) {
6865
- console.log(` ${pc3.dim(`(Could not auto-open browser: ${openResult.error})`)}`);
6866
- }
6867
- } else {
6868
- const openAnswer = await clack.confirm({
6869
- message: `Open ${commandUrl} in your browser now?`,
6870
- initialValue: true
6871
- });
6872
- if (!clack.isCancel(openAnswer) && openAnswer === true) {
6873
- const openResult = openBrowser(commandUrl);
6874
- if (!openResult.ok && openResult.error) {
6875
- console.log(` ${pc3.dim(`(Could not auto-open browser: ${openResult.error})`)}`);
6876
- }
6877
- }
6878
- }
6879
- return "completed";
6880
- }
6881
6867
  function printAuthStatus(status) {
6882
6868
  if (!status.configured) {
6883
6869
  console.log(` ${ICON.warn} ${pc3.yellow("no account")} run ${pc3.cyan(`${getCmd()} auth login`)} to connect`);
@@ -6956,12 +6942,12 @@ function printDoctorReport(report, assessment) {
6956
6942
  async function main() {
6957
6943
  const program = new Command();
6958
6944
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
6959
- const pkgVersion = true ? "0.1.20" : void 0;
6945
+ const pkgVersion = true ? "0.1.21" : void 0;
6960
6946
  program.version(pkgVersion ?? "unknown", "-V, --version");
6961
6947
  program.hook("preAction", () => {
6962
6948
  console.log(renderBanner(pkgVersion));
6963
6949
  });
6964
- 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) => {
6950
+ 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) => {
6965
6951
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
6966
6952
  await safeTrackWizardTelemetry("wizard_started", {
6967
6953
  command: "setup",
@@ -7183,12 +7169,11 @@ async function main() {
7183
7169
  } else if (briefResult.status === "failed") {
7184
7170
  console.log(` ${ICON.warn} ${pc3.yellow("daily brief")} ${pc3.dim(briefResult.message)}`);
7185
7171
  }
7186
- const peopleFirstResult = await maybeRunPeopleFirstCapture({
7172
+ const intentResult = await maybeCaptureSetupIntent({
7187
7173
  interactive,
7188
- openInBrowser: Boolean(options.open),
7189
7174
  workspace: resolvedWorkspace
7190
7175
  });
7191
- if (peopleFirstResult === "cancelled") {
7176
+ if (intentResult === "cancelled") {
7192
7177
  return;
7193
7178
  }
7194
7179
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
@@ -7608,6 +7593,17 @@ async function main() {
7608
7593
  persistContinuityDefaults({ workspace: result.workspace });
7609
7594
  printWorkspace(result.workspace);
7610
7595
  });
7596
+ program.command("intent").description(
7597
+ "Capture an intent, preview the classified agent + plan, and approve or hand off to the browser."
7598
+ ).argument("[text...]", "intent text (quotes optional)").option("--workspace <id>", "workspace id override; defaults to the current workspace").option("--json", "emit a JSON summary instead of the box-drawn preview").action(async (textParts, options) => {
7599
+ const initialText = textParts.join(" ").trim();
7600
+ const workspaceId = options.workspace?.trim() ?? "";
7601
+ await runIntentCommand({
7602
+ ...initialText ? { initialText } : {},
7603
+ ...workspaceId ? { workspaceId } : {},
7604
+ jsonOutput: Boolean(options.json)
7605
+ });
7606
+ });
7611
7607
  program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
7612
7608
  const spinner = createOrgxSpinner("Running OrgX health check");
7613
7609
  spinner.start();