@useorgx/wizard 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +1286 -642
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3,7 +3,9 @@
3
3
  // src/cli.ts
4
4
  import * as clack from "@clack/prompts";
5
5
  import { spawnSync as spawnSync3 } from "child_process";
6
+ import { readFileSync as readFileSync3 } from "fs";
6
7
  import { hostname } from "os";
8
+ import { resolve } from "path";
7
9
  import { Command } from "commander";
8
10
  import pc3 from "picocolors";
9
11
 
@@ -791,7 +793,7 @@ function parsePairingPollResult(value) {
791
793
  };
792
794
  }
793
795
  function sleep(ms) {
794
- return new Promise((resolve) => setTimeout(resolve, ms));
796
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
795
797
  }
796
798
  async function startBrowserPairing(options, fetchImpl) {
797
799
  const data = await fetchJson({
@@ -904,12 +906,12 @@ h1{font-size:1.5rem;color:#b91c1c}p{color:#555;margin-top:.5rem}</style>
904
906
  <p>Return to your terminal and try again.</p>
905
907
  </div></body></html>`;
906
908
  function tryListen(port, hostname2) {
907
- return new Promise((resolve, reject) => {
909
+ return new Promise((resolve2, reject) => {
908
910
  const server = createServer();
909
911
  server.once("error", reject);
910
912
  server.listen(port, hostname2, () => {
911
913
  server.removeListener("error", reject);
912
- resolve(server);
914
+ resolve2(server);
913
915
  });
914
916
  });
915
917
  }
@@ -938,7 +940,7 @@ async function startLocalAuthServer(options) {
938
940
  const successHtml = options.successHtml ?? DEFAULT_SUCCESS_HTML;
939
941
  const errorHtml = options.errorHtml ?? DEFAULT_ERROR_HTML;
940
942
  const { server, port } = await bindServer(options.preferredPort, hostname2);
941
- const result = new Promise((resolve, reject) => {
943
+ const result = new Promise((resolve2, reject) => {
942
944
  const timer = setTimeout(() => {
943
945
  server.close();
944
946
  reject(new Error("Timed out waiting for browser authorization."));
@@ -981,7 +983,7 @@ async function startLocalAuthServer(options) {
981
983
  res.writeHead(200, { "Content-Type": "text/html" }).end(successHtml);
982
984
  clearTimeout(timer);
983
985
  server.close();
984
- resolve({ code, state });
986
+ resolve2({ code, state });
985
987
  });
986
988
  });
987
989
  return { port, result };
@@ -1389,28 +1391,6 @@ function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
1389
1391
  if (peopleFirstCapture !== void 0) state.peopleFirstCapture = peopleFirstCapture;
1390
1392
  return state;
1391
1393
  }
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
1394
  function writeWizardState(value, statePath = ORGX_WIZARD_STATE_PATH) {
1415
1395
  const record = sanitizeWizardStateRecord(value);
1416
1396
  writeJsonFile(statePath, record, { mode: 384 });
@@ -4457,7 +4437,7 @@ function formatCommandFailure(command, args, result) {
4457
4437
  return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
4458
4438
  }
4459
4439
  async function defaultCommandRunner(command, args) {
4460
- return await new Promise((resolve) => {
4440
+ return await new Promise((resolve2) => {
4461
4441
  const child = spawn(command, [...args], {
4462
4442
  env: process.env,
4463
4443
  stdio: ["ignore", "pipe", "pipe"]
@@ -4472,7 +4452,7 @@ async function defaultCommandRunner(command, args) {
4472
4452
  });
4473
4453
  child.on("error", (error) => {
4474
4454
  const errorCode = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
4475
- resolve({
4455
+ resolve2({
4476
4456
  exitCode: -1,
4477
4457
  stdout,
4478
4458
  stderr,
@@ -4480,7 +4460,7 @@ async function defaultCommandRunner(command, args) {
4480
4460
  });
4481
4461
  });
4482
4462
  child.on("close", (code) => {
4483
- resolve({
4463
+ resolve2({
4484
4464
  exitCode: code ?? -1,
4485
4465
  stdout,
4486
4466
  stderr
@@ -5403,7 +5383,357 @@ async function trackWizardTelemetry(event, properties = {}, options = {}) {
5403
5383
  return response?.ok === true;
5404
5384
  }
5405
5385
 
5386
+ // src/lib/intents.ts
5387
+ var AGENT_SLUGS = [
5388
+ "mark",
5389
+ "eli",
5390
+ "dana",
5391
+ "pace",
5392
+ "sage",
5393
+ "orion",
5394
+ "xandy"
5395
+ ];
5396
+ var INTENT_STATUSES = [
5397
+ "pending",
5398
+ "approved",
5399
+ "adjusted",
5400
+ "archived"
5401
+ ];
5402
+ var AGENT_ROLES = {
5403
+ mark: "Partnerships",
5404
+ eli: "Engineering",
5405
+ dana: "Brand",
5406
+ pace: "Product",
5407
+ sage: "Strategy",
5408
+ orion: "Routing",
5409
+ xandy: "Default"
5410
+ };
5411
+ var CONFIDENCE_DECIMAL = {
5412
+ low: 0.55,
5413
+ med: 0.78,
5414
+ high: 0.92
5415
+ };
5416
+ var AGENT_ANSI_256 = {
5417
+ pace: 34,
5418
+ // green
5419
+ eli: 44,
5420
+ // cyan
5421
+ mark: 208,
5422
+ // orange
5423
+ sage: 135,
5424
+ // purple
5425
+ orion: 214,
5426
+ // amber
5427
+ dana: 199,
5428
+ // pink
5429
+ xandy: 37
5430
+ // teal
5431
+ };
5432
+ function parseAgentSlug(value) {
5433
+ if (typeof value !== "string") return null;
5434
+ return AGENT_SLUGS.includes(value) ? value : null;
5435
+ }
5436
+ function parseStatus(value) {
5437
+ if (typeof value !== "string") return null;
5438
+ return INTENT_STATUSES.includes(value) ? value : null;
5439
+ }
5440
+ function parseConfidence(value) {
5441
+ if (value === "low" || value === "med" || value === "high") return value;
5442
+ return "med";
5443
+ }
5444
+ function parsePlanStep(value) {
5445
+ if (!isRecord(value)) return null;
5446
+ const title = typeof value.title === "string" ? value.title.trim() : "";
5447
+ if (!title) return null;
5448
+ const detail = typeof value.detail === "string" && value.detail.trim().length > 0 ? value.detail : void 0;
5449
+ const agentSlug = parseAgentSlug(value.agent_slug);
5450
+ const step = { title };
5451
+ if (detail !== void 0) step.detail = detail;
5452
+ if (agentSlug !== null) step.agent_slug = agentSlug;
5453
+ return step;
5454
+ }
5455
+ function parsePlanPreview(value) {
5456
+ if (!isRecord(value)) {
5457
+ return { steps: [], eta: "under a week", confidence: "med" };
5458
+ }
5459
+ const steps = Array.isArray(value.steps) ? value.steps.map(parsePlanStep).filter((step) => step !== null) : [];
5460
+ const eta = typeof value.eta === "string" && value.eta.trim().length > 0 ? value.eta : "under a week";
5461
+ return {
5462
+ steps,
5463
+ eta,
5464
+ confidence: parseConfidence(value.confidence)
5465
+ };
5466
+ }
5467
+ function parseIntent(value) {
5468
+ if (!isRecord(value)) return null;
5469
+ const id = typeof value.id === "string" ? value.id : null;
5470
+ const workspaceId = typeof value.workspace_id === "string" ? value.workspace_id : null;
5471
+ const slug = typeof value.slug === "string" ? value.slug : null;
5472
+ const text2 = typeof value.text === "string" ? value.text : null;
5473
+ const status = parseStatus(value.status);
5474
+ const createdAt = typeof value.created_at === "string" ? value.created_at : null;
5475
+ const updatedAt = typeof value.updated_at === "string" ? value.updated_at : null;
5476
+ if (!id || !workspaceId || !slug || !text2 || !status || !createdAt || !updatedAt) {
5477
+ return null;
5478
+ }
5479
+ return {
5480
+ id,
5481
+ workspace_id: workspaceId,
5482
+ slug,
5483
+ text: text2,
5484
+ status,
5485
+ suggested_agent_slug: parseAgentSlug(value.suggested_agent_slug),
5486
+ plan_preview: parsePlanPreview(value.plan_preview),
5487
+ approved_at: typeof value.approved_at === "string" ? value.approved_at : null,
5488
+ created_at: createdAt,
5489
+ updated_at: updatedAt
5490
+ };
5491
+ }
5492
+ async function parseResponseBody4(response) {
5493
+ const text2 = await response.text();
5494
+ if (!text2) return null;
5495
+ try {
5496
+ return JSON.parse(text2);
5497
+ } catch {
5498
+ return text2;
5499
+ }
5500
+ }
5501
+ function formatHttpError3(status, body) {
5502
+ if (typeof body === "string" && body.trim().length > 0) {
5503
+ return `HTTP ${status}: ${body}`;
5504
+ }
5505
+ if (isRecord(body)) {
5506
+ if (isRecord(body.error)) {
5507
+ const message = typeof body.error.message === "string" ? body.error.message : null;
5508
+ const code = typeof body.error.code === "string" ? body.error.code : null;
5509
+ if (message) return `HTTP ${status} (${code ?? "error"}): ${message}`;
5510
+ }
5511
+ if (typeof body.error === "string" && body.error.trim().length > 0) {
5512
+ return `HTTP ${status}: ${body.error}`;
5513
+ }
5514
+ }
5515
+ return `HTTP ${status}`;
5516
+ }
5517
+ async function requireOrgxAuth3(options = {}) {
5518
+ const auth = await resolveOrgxAuth(options);
5519
+ if (!auth) {
5520
+ throw new Error(
5521
+ "No OrgX API key configured. Run `orgx-wizard auth login` or `orgx-wizard auth set-key <oxk_...>` first."
5522
+ );
5523
+ }
5524
+ return auth;
5525
+ }
5526
+ async function createIntent(input, options = {}) {
5527
+ const text2 = input.text.trim();
5528
+ if (!text2) {
5529
+ throw new Error("Intent text is required.");
5530
+ }
5531
+ if (text2.length > 4e3) {
5532
+ throw new Error("Intent text exceeds 4,000 characters.");
5533
+ }
5534
+ const workspaceId = input.workspace_id.trim();
5535
+ if (!workspaceId) {
5536
+ throw new Error("Workspace id is required.");
5537
+ }
5538
+ const auth = await requireOrgxAuth3(options);
5539
+ const url = buildOrgxApiUrl("/v1/intents", auth.baseUrl);
5540
+ const response = await fetch(url, {
5541
+ method: "POST",
5542
+ headers: {
5543
+ Authorization: `Bearer ${auth.apiKey}`,
5544
+ "Content-Type": "application/json"
5545
+ },
5546
+ body: JSON.stringify({ workspace_id: workspaceId, text: text2 }),
5547
+ signal: AbortSignal.timeout(15e3)
5548
+ });
5549
+ const body = await parseResponseBody4(response);
5550
+ if (!response.ok) {
5551
+ throw new Error(
5552
+ `Failed to create intent. ${formatHttpError3(response.status, body)}`
5553
+ );
5554
+ }
5555
+ const intent = parseIntent(body);
5556
+ if (!intent) {
5557
+ throw new Error("OrgX returned an unexpected intent payload.");
5558
+ }
5559
+ return intent;
5560
+ }
5561
+ async function updateIntent(id, input, options = {}) {
5562
+ const trimmedId = id.trim();
5563
+ if (!trimmedId) {
5564
+ throw new Error("Intent id is required.");
5565
+ }
5566
+ const auth = await requireOrgxAuth3(options);
5567
+ const url = buildOrgxApiUrl(`/v1/intents/${encodeURIComponent(trimmedId)}`, auth.baseUrl);
5568
+ const response = await fetch(url, {
5569
+ method: "PATCH",
5570
+ headers: {
5571
+ Authorization: `Bearer ${auth.apiKey}`,
5572
+ "Content-Type": "application/json"
5573
+ },
5574
+ body: JSON.stringify(input),
5575
+ signal: AbortSignal.timeout(15e3)
5576
+ });
5577
+ const body = await parseResponseBody4(response);
5578
+ if (!response.ok) {
5579
+ throw new Error(
5580
+ `Failed to update intent. ${formatHttpError3(response.status, body)}`
5581
+ );
5582
+ }
5583
+ const intent = parseIntent(body);
5584
+ if (!intent) {
5585
+ throw new Error("OrgX returned an unexpected intent update payload.");
5586
+ }
5587
+ return intent;
5588
+ }
5589
+ function buildIntentHandoffUrl(slug, options = {}) {
5590
+ const base = options.baseUrl?.trim() || "https://useorgx.com";
5591
+ const normalized = base.replace(/\/+$/, "");
5592
+ return `${normalized}/command?intent=${encodeURIComponent(slug)}`;
5593
+ }
5594
+
5595
+ // src/lib/intent-render.ts
5596
+ function detectRenderCapabilities(env = process.env) {
5597
+ const noColor = Boolean(env.NO_COLOR && env.NO_COLOR.length > 0);
5598
+ const asciiOnly = (env.LC_ALL ?? "").toUpperCase() === "C" || (env.LANG ?? "").toUpperCase() === "C";
5599
+ return { noColor, asciiOnly };
5600
+ }
5601
+ function isInteractive(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
5602
+ if (!isTTY) return false;
5603
+ if (env.CI === "1" || env.CI === "true") return false;
5604
+ if (env.GITHUB_ACTIONS === "true") return false;
5605
+ return true;
5606
+ }
5607
+ var CSI = "\x1B[";
5608
+ function wrap(code, text2, enabled) {
5609
+ if (!enabled) return text2;
5610
+ return `${CSI}${code}m${text2}${CSI}0m`;
5611
+ }
5612
+ function colorize(text2, ansi256, options = {}) {
5613
+ return wrap(`38;5;${ansi256}`, text2, !options.noColor);
5614
+ }
5615
+ function bold(text2, options = {}) {
5616
+ return wrap("1", text2, !options.noColor);
5617
+ }
5618
+ function dim(text2, options = {}) {
5619
+ return wrap("2", text2, !options.noColor);
5620
+ }
5621
+ function formatConfidence(value) {
5622
+ const bounded = Math.max(0, Math.min(1, value));
5623
+ return bounded.toFixed(2);
5624
+ }
5625
+ function renderRoutingLine(agent, confidence, options = {}) {
5626
+ const arrow = options.asciiOnly ? "->" : "\u2192";
5627
+ const separator = options.asciiOnly ? " - " : " \xB7 ";
5628
+ const role = AGENT_ROLES[agent];
5629
+ const name = agent.charAt(0).toUpperCase() + agent.slice(1);
5630
+ const coloredName = colorize(name, AGENT_ANSI_256[agent], options);
5631
+ const confidenceLabel = dim("confidence", options);
5632
+ const confidenceValue = formatConfidence(confidence);
5633
+ return ` ${arrow} ${bold(coloredName, options)}${separator}${role} ${confidenceLabel} ${confidenceValue}`;
5634
+ }
5635
+ function resolveConfidenceDecimal(plan) {
5636
+ return CONFIDENCE_DECIMAL[plan.confidence];
5637
+ }
5638
+ function renderPlanPreview(plan, options = {}) {
5639
+ const horizontal = options.asciiOnly ? "-" : "\u2500";
5640
+ const topLeft = options.asciiOnly ? "+" : "\u250C";
5641
+ const topRight = options.asciiOnly ? "+" : "\u2510";
5642
+ const bottomLeft = options.asciiOnly ? "+" : "\u2514";
5643
+ const bottomRight = options.asciiOnly ? "+" : "\u2518";
5644
+ const vertical = options.asciiOnly ? "|" : "\u2502";
5645
+ const steps = plan.steps.map((step, index) => {
5646
+ const number = `${index + 1}.`.padEnd(3, " ");
5647
+ return `${number} ${step.title}`;
5648
+ });
5649
+ const etaLine = `ETA ${plan.eta}`;
5650
+ const lines = [...steps, "", etaLine];
5651
+ const label = "Plan preview";
5652
+ const contentWidth = Math.max(
5653
+ // Keep enough room for the top-chrome label plus its "─ " prefix and
5654
+ // a minimum trailing dash so the border visually wraps the label.
5655
+ label.length + 4,
5656
+ ...lines.map((line) => line.length)
5657
+ );
5658
+ const innerWidth = contentWidth + 2;
5659
+ const leadingDashes = horizontal.repeat(1);
5660
+ const trailingDashes = horizontal.repeat(
5661
+ Math.max(1, innerWidth - label.length - 3)
5662
+ // leading dash + 2 spaces
5663
+ );
5664
+ const top = `${topLeft}${leadingDashes} ${label} ${trailingDashes}${topRight}`;
5665
+ const body = lines.map((line) => {
5666
+ const padded = line.padEnd(contentWidth, " ");
5667
+ return `${vertical} ${padded} ${vertical}`;
5668
+ }).join("\n");
5669
+ const bottom = `${bottomLeft}${horizontal.repeat(innerWidth)}${bottomRight}`;
5670
+ return [top, body, bottom].join("\n");
5671
+ }
5672
+ var TONE_ANSI = {
5673
+ primary: 190,
5674
+ // lime (--ox-primary)
5675
+ warning: 214,
5676
+ // amber (adjust)
5677
+ danger: 203,
5678
+ // rose (archive)
5679
+ neutral: 244
5680
+ // bright black (quit)
5681
+ };
5682
+ function renderHotkeyBar(entries, options = {}) {
5683
+ const prefix = options.asciiOnly ? ">>" : ">>";
5684
+ const segments = entries.map((entry) => {
5685
+ const tone = entry.tone ?? "neutral";
5686
+ const keyLabel = `[${entry.key.toUpperCase()}]`;
5687
+ const coloredKey = colorize(keyLabel, TONE_ANSI[tone], options);
5688
+ return `${coloredKey}${entry.label}`;
5689
+ });
5690
+ return `${prefix} ${segments.join(" ")}`;
5691
+ }
5692
+ var DEFAULT_HOTKEYS = [
5693
+ { key: "a", label: "pprove", tone: "primary" },
5694
+ { key: "e", label: "dit in browser", tone: "warning" },
5695
+ { key: "x", label: "archive", tone: "danger" },
5696
+ { key: "q", label: "uit", tone: "neutral" }
5697
+ ];
5698
+ function normalizeHotkey(input) {
5699
+ const trimmed = input.trim().toLowerCase();
5700
+ if (trimmed.length === 0) return null;
5701
+ const ch = trimmed.charAt(0);
5702
+ switch (ch) {
5703
+ case "a":
5704
+ return "approve";
5705
+ case "e":
5706
+ return "edit";
5707
+ case "x":
5708
+ return "archive";
5709
+ case "q":
5710
+ return "quit";
5711
+ default:
5712
+ return null;
5713
+ }
5714
+ }
5715
+
5406
5716
  // src/lib/daily-brief-onboarding.ts
5717
+ function extractErrorHint(body) {
5718
+ if (!body) return null;
5719
+ let parsed;
5720
+ try {
5721
+ parsed = JSON.parse(body);
5722
+ } catch {
5723
+ return body.slice(0, 160);
5724
+ }
5725
+ if (!parsed || typeof parsed !== "object") return null;
5726
+ const obj = parsed;
5727
+ const err = obj.error;
5728
+ if (typeof err === "string") return err;
5729
+ if (err && typeof err === "object") {
5730
+ const message = err.message;
5731
+ if (typeof message === "string") return message;
5732
+ const code = err.code;
5733
+ if (typeof code === "string") return code;
5734
+ }
5735
+ return null;
5736
+ }
5407
5737
  var BASELINE_PROMPTS = [
5408
5738
  { task_type: "code_review", label: "Code review", placeholder: "20" },
5409
5739
  { task_type: "prd_draft", label: "PRD draft", placeholder: "45" },
@@ -5563,9 +5893,10 @@ async function runDailyBriefOnboarding(options) {
5563
5893
  });
5564
5894
  if (!commitResponse.ok) {
5565
5895
  const text2 = await commitResponse.text().catch(() => "");
5896
+ const hint = extractErrorHint(text2) ?? `HTTP ${commitResponse.status}`;
5566
5897
  return {
5567
5898
  status: "failed",
5568
- message: "Could not commit onboarding capture.",
5899
+ message: `Could not commit onboarding capture \u2014 ${hint}`,
5569
5900
  error: `HTTP ${commitResponse.status}: ${text2.slice(0, 200)}`
5570
5901
  };
5571
5902
  }
@@ -5596,534 +5927,501 @@ async function fetchOnboardingState(auth) {
5596
5927
  }
5597
5928
  }
5598
5929
 
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" }
5930
+ // src/lib/self-audit.ts
5931
+ import { createHash as createHash3 } from "crypto";
5932
+ var SELF_AUDIT_SCHEMA_VERSION = "2026-04-27";
5933
+ var AUDIT_DIMENSIONS = [
5934
+ "queryability",
5935
+ "proof_density",
5936
+ "loop_closure",
5937
+ "context_debt",
5938
+ "autonomy_readiness",
5939
+ "roi_visibility"
5608
5940
  ];
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;
5941
+ function clampScore(value) {
5942
+ return Math.max(0, Math.min(100, Math.round(value)));
5943
+ }
5944
+ function ratio(numerator, denominator, fallback = 0) {
5945
+ if (denominator <= 0) return fallback;
5946
+ return numerator / denominator;
5947
+ }
5948
+ function includesAny(value, patterns) {
5949
+ return patterns.some((pattern) => pattern.test(value));
5950
+ }
5951
+ function classifyLine(line) {
5952
+ const normalized = line.trim();
5953
+ if (!normalized) return null;
5954
+ if (/^(decision|decided|we decided|d:)\b/i.test(normalized)) return "decision";
5955
+ if (/^(commitment|committed|promise|promised|todo:|we will)\b/i.test(normalized)) return "commitment";
5956
+ if (/^(artifact|receipt|proof|shipped|implemented|commit|pr:)\b/i.test(normalized)) return "artifact";
5957
+ if (/^(open loop|gap|blocker|risk|missing|needs|unresolved)\b/i.test(normalized)) return "open_loop";
5958
+ if (/^(next action|follow[- ]?up|next step|action:)\b/i.test(normalized)) return "next_action";
5959
+ if (/^(outcome|result|impact|metric|adoption)\b/i.test(normalized)) return "outcome";
5960
+ if (/^(roi|economics|token|tokens|cost|saved|time saved|api bill)\b/i.test(normalized)) return "economics";
5961
+ return null;
5962
+ }
5963
+ function extractFounderLoopItems(imports) {
5964
+ const items = [];
5965
+ for (const source of imports) {
5966
+ const lines = source.text.split(/\r?\n/);
5967
+ lines.forEach((line, index) => {
5968
+ const type = classifyLine(line);
5969
+ if (!type) return;
5970
+ const lineNumber = index + 1;
5971
+ items.push({
5972
+ evidenceRef: `${source.sourceId}:L${lineNumber}`,
5973
+ lineNumber,
5974
+ sourceId: source.sourceId,
5975
+ sourceLabel: source.sourceLabel,
5976
+ text: line.trim(),
5977
+ type
5978
+ });
5979
+ });
5630
5980
  }
5981
+ return items;
5982
+ }
5983
+ function buildSelfAuditSignals(imports, items, options = {}) {
5984
+ const allText = imports.map((source) => source.text).join("\n");
5985
+ const lower = allText.toLowerCase();
5986
+ const decisions = items.filter((item) => item.type === "decision");
5987
+ const artifacts = items.filter((item) => item.type === "artifact");
5988
+ const commitments = items.filter((item) => item.type === "commitment");
5989
+ const nextActions = items.filter((item) => item.type === "next_action");
5990
+ const outcomes = items.filter((item) => item.type === "outcome");
5991
+ const economics = items.filter((item) => item.type === "economics");
5992
+ const proofMentions = (lower.match(/\b(proof|verified|verification|receipt|quality score|artifact)\b/g) ?? []).length;
5993
+ const ownerMentions = (lower.match(/\b(owner|dri|responsible|agent:|founder)\b/g) ?? []).length;
5994
+ const artifactNextActionMentions = artifacts.filter(
5995
+ (item) => includesAny(item.text.toLowerCase(), [/\bnext action\b/, /\bfollow[- ]?up\b/, /\brollback\b/])
5996
+ ).length;
5997
+ const writebackMentions = (lower.match(/\b(approve|approved|writeback|rollback|create task|follow-up task)\b/g) ?? []).length;
5998
+ const repeatedContextPrompts = (lower.match(/\b(recap|catch you up|context again|restate|reread|remind me)\b/g) ?? []).length;
5999
+ return {
6000
+ approvedWritebackTargets: Math.min(3, writebackMentions),
6001
+ artifactsWithNextActions: Math.min(artifacts.length, nextActions.length + artifactNextActionMentions),
6002
+ artifactsWithOwners: Math.min(artifacts.length, ownerMentions),
6003
+ completedWorkItems: artifacts.length,
6004
+ completedWorkWithProof: Math.min(artifacts.length, proofMentions),
6005
+ connectedSources: Math.max(options.connectedSources?.length ?? 0, imports.length),
6006
+ decisionsWithEvidence: decisions.length,
6007
+ economicSignals: economics.length + (lower.includes("token") || lower.includes("cost") || lower.includes("saved") ? 1 : 0),
6008
+ missingSources: options.missingSources?.length ?? 0,
6009
+ openLoops: items.filter((item) => item.type === "open_loop").length,
6010
+ outcomeLinkedItems: outcomes.length,
6011
+ repeatedContextPrompts,
6012
+ totalArtifacts: artifacts.length,
6013
+ totalContextItems: Math.max(items.length, allText.split(/\s+/).filter(Boolean).length),
6014
+ totalDecisions: decisions.length,
6015
+ unresolvedCommitments: Math.max(0, commitments.length - nextActions.length - outcomes.length)
6016
+ };
5631
6017
  }
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 });
6018
+ function scoreSelfAudit(signals) {
6019
+ const queryability = clampScore(
6020
+ 30 + ratio(signals.decisionsWithEvidence, Math.max(1, signals.totalDecisions), 0) * 25 + Math.min(20, signals.connectedSources * 5) + Math.min(30, (signals.totalArtifacts + signals.outcomeLinkedItems + signals.approvedWritebackTargets) * 5)
6021
+ );
6022
+ const proofDensity = clampScore(
6023
+ 30 + ratio(signals.completedWorkWithProof, Math.max(1, signals.completedWorkItems), 0) * 35 + ratio(signals.artifactsWithOwners, Math.max(1, signals.totalArtifacts), 0) * 20 + ratio(signals.artifactsWithNextActions, Math.max(1, signals.totalArtifacts), 0) * 15
6024
+ );
6025
+ const loopClosure = clampScore(
6026
+ 40 + Math.min(25, signals.outcomeLinkedItems * 25) + Math.min(35, signals.approvedWritebackTargets * 20) + (signals.openLoops === 0 ? 15 : Math.max(0, 15 - signals.openLoops * 3))
6027
+ );
6028
+ const contextDebt = clampScore(
6029
+ 100 - Math.min(35, signals.repeatedContextPrompts * 10) - Math.min(35, signals.openLoops * 6) - Math.min(20, signals.missingSources * 4) - Math.min(10, signals.unresolvedCommitments * 3)
6030
+ );
6031
+ const autonomyReadiness = clampScore(
6032
+ 35 + Math.min(30, signals.approvedWritebackTargets * 15) + Math.min(20, signals.completedWorkWithProof * 5) + (signals.openLoops <= 1 ? 15 : 5)
6033
+ );
6034
+ const roiVisibility = clampScore(
6035
+ 30 + Math.min(40, signals.economicSignals * 18) + Math.min(25, signals.outcomeLinkedItems * 25) + Math.min(10, signals.completedWorkWithProof * 3)
6036
+ );
6037
+ return {
6038
+ autonomy_readiness: autonomyReadiness,
6039
+ context_debt: contextDebt,
6040
+ loop_closure: loopClosure,
6041
+ proof_density: proofDensity,
6042
+ queryability,
6043
+ roi_visibility: roiVisibility
6044
+ };
6045
+ }
6046
+ function buildFindings(scores, signals) {
6047
+ const findings = [];
6048
+ for (const dimension of AUDIT_DIMENSIONS) {
6049
+ const score = scores[dimension];
6050
+ if (score >= 95) {
6051
+ findings.push({
6052
+ dimension,
6053
+ evidence: `Score ${score}/100 meets the Phase 2 target.`,
6054
+ recommendation: "Keep this dimension gated by evidence so the score stays earned.",
6055
+ severity: "info",
6056
+ title: `${dimension.replace(/_/g, " ")} is at the 95+ target`
6057
+ });
6058
+ continue;
5644
6059
  }
6060
+ const recommendationByDimension = {
6061
+ autonomy_readiness: "Keep writeback approval-gated and add rollback references to every generated action.",
6062
+ context_debt: "Reduce repeated context prompts and close unresolved open loops with task or decision artifacts.",
6063
+ loop_closure: "Add outcome references and approved follow-up writebacks so planning changes after execution.",
6064
+ proof_density: "Attach owners, next actions, and verification proof to every completed work artifact.",
6065
+ queryability: "Add cited decisions, artifacts, connected sources, and retrieval scope to the audit output.",
6066
+ roi_visibility: "Add token/time/cost evidence plus an outcome review so ROI is not a narrative claim."
6067
+ };
6068
+ findings.push({
6069
+ dimension,
6070
+ evidence: `Score ${score}/100. Signals: ${JSON.stringify({
6071
+ approvedWritebackTargets: signals.approvedWritebackTargets,
6072
+ completedWorkWithProof: signals.completedWorkWithProof,
6073
+ economicSignals: signals.economicSignals,
6074
+ openLoops: signals.openLoops,
6075
+ outcomeLinkedItems: signals.outcomeLinkedItems,
6076
+ repeatedContextPrompts: signals.repeatedContextPrompts
6077
+ })}`,
6078
+ recommendation: recommendationByDimension[dimension],
6079
+ severity: score < 60 ? "critical" : "warning",
6080
+ title: `${dimension.replace(/_/g, " ")} needs evidence before it can be called 95+`
6081
+ });
5645
6082
  }
5646
- return channels;
6083
+ return findings;
5647
6084
  }
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;
6085
+ function buildSelfCritique(scores) {
6086
+ return AUDIT_DIMENSIONS.map((dimension) => {
6087
+ const score = scores[dimension];
6088
+ return {
6089
+ dimension,
6090
+ gap: score >= 95 ? "No score gap. Preserve evidence and regression-test this dimension." : `Needs ${95 - score} more points of verified product evidence before claiming 95+.`,
6091
+ passed: score >= 95,
6092
+ score,
6093
+ target: 95
6094
+ };
6095
+ });
6096
+ }
6097
+ function hashPlanPayload(payload) {
6098
+ return createHash3("sha256").update(JSON.stringify(payload)).digest("hex");
6099
+ }
6100
+ function buildSelfAuditPlan(input) {
6101
+ if (input.imports.length === 0) {
6102
+ throw new Error("At least one AI-session import is required to run the Founder Loop audit.");
6103
+ }
6104
+ const items = extractFounderLoopItems(input.imports);
6105
+ const connectedSources = input.connectedSources ?? input.imports.map((source) => source.sourceLabel);
6106
+ const missingSources = input.missingSources ?? [];
6107
+ const signals = buildSelfAuditSignals(input.imports, items, {
6108
+ connectedSources,
6109
+ missingSources
6110
+ });
6111
+ const scores = scoreSelfAudit(signals);
6112
+ const evidenceRefs = items.slice(0, 12).map((item) => item.evidenceRef);
6113
+ const decisions = items.filter((item) => item.type === "decision");
6114
+ const nextAction = items.find((item) => item.type === "next_action");
6115
+ const basePlan = {
6116
+ artifact_type: "ai_native_self_audit_plan",
6117
+ audit_scope: {
6118
+ connected_sources: connectedSources,
6119
+ loop: "founder",
6120
+ missing_sources: missingSources,
6121
+ time_window_days: input.timeWindowDays ?? 30
6122
+ },
6123
+ extracted_items: items,
6124
+ findings: buildFindings(scores, signals),
6125
+ generated_at: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
6126
+ recommended_follow_up: {
6127
+ rollback: "Delete or close the generated OrgX follow-up task if the founder rejects the audit recommendation.",
6128
+ summary: nextAction?.text ?? "Review the audit findings, approve one follow-up action, and attach proof after execution.",
6129
+ title: "Review AI-native self-audit findings and approve first follow-up"
6130
+ },
6131
+ recommended_initiative: {
6132
+ summary: "Generated by @useorgx/wizard audit to close the first Founder Loop from AI-session context, proof artifacts, approved writeback, and outcome review.",
6133
+ title: `Close Founder Loop: ${input.workspace.name} AI-native operating loop`,
6134
+ workstreams: [
6135
+ {
6136
+ purpose: "Capture AI-session context, decisions, commitments, and open loops.",
6137
+ tasks: [
6138
+ {
6139
+ proof_requirement: "AI-session import with cited evidence references.",
6140
+ title: "Import founder AI-session context"
6141
+ }
6142
+ ],
6143
+ title: "Founder Context Ingest"
6144
+ },
6145
+ {
6146
+ purpose: "Turn completed work into artifacts with owners, proof, and next actions.",
6147
+ tasks: [
6148
+ {
6149
+ proof_requirement: "Artifact metadata includes owner, evidence refs, and retrieval scope.",
6150
+ title: "Attach proof artifacts to completed work"
6151
+ }
6152
+ ],
6153
+ title: "Proof Chain"
6154
+ },
6155
+ {
6156
+ purpose: "Write one approved follow-up action into OrgX with rollback context.",
6157
+ tasks: [
6158
+ {
6159
+ proof_requirement: "Approved OrgX task with rollback and evidence references.",
6160
+ title: "Approve first OrgX follow-up writeback"
6161
+ }
6162
+ ],
6163
+ title: "Safe Writeback"
6164
+ },
6165
+ {
6166
+ purpose: "Record outcome and economics so the next plan learns from execution.",
6167
+ tasks: [
6168
+ {
6169
+ proof_requirement: "Founder review with time/token/value estimate and attribution confidence.",
6170
+ title: "Record outcome and economics review"
6171
+ }
6172
+ ],
6173
+ title: "Outcome Review"
6174
+ }
6175
+ ]
6176
+ },
6177
+ safe_writeback_plan: [
6178
+ {
6179
+ action: "create_follow_up_task",
6180
+ approval: "required",
6181
+ rollback: "Delete or close the generated task and preserve the audit artifact as a rejected recommendation.",
6182
+ target: "orgx"
5666
6183
  }
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 };
6184
+ ],
6185
+ schema_version: SELF_AUDIT_SCHEMA_VERSION,
6186
+ scores,
6187
+ self_critique: buildSelfCritique(scores),
6188
+ signals,
6189
+ workspace: input.workspace
6190
+ };
6191
+ const invariant = {
6192
+ decision_refs: decisions.map((item) => item.evidenceRef),
6193
+ evidence_refs: evidenceRefs,
6194
+ goal_ref: `workspace:${input.workspace.id}:ai-native-self-audit`,
6195
+ next_action_ref: nextAction?.evidenceRef ?? "manual-next-action:review-audit",
6196
+ owner_ref: "founder:dri",
6197
+ proof_requirement: "verification",
6198
+ retrieval_scope: "Retrieve the imported AI-session context, extracted Founder Loop items, audit scores, findings, writeback plan, and outcome review."
6199
+ };
6200
+ const artifactHash = hashPlanPayload({ ...basePlan, artifact_invariant: invariant });
6201
+ return {
6202
+ ...basePlan,
6203
+ artifact_hash: artifactHash,
6204
+ artifact_invariant: invariant
6205
+ };
6206
+ }
6207
+ function renderSelfAuditMarkdown(plan) {
6208
+ const lines = [
6209
+ "# AI-Native Founder Loop Self-Audit",
6210
+ "",
6211
+ `Generated: ${plan.generated_at}`,
6212
+ `Workspace: ${plan.workspace.name} (${plan.workspace.id})`,
6213
+ `Artifact hash: ${plan.artifact_hash}`,
6214
+ "",
6215
+ "## Scores",
6216
+ "",
6217
+ "| Dimension | Score | Target | Status |",
6218
+ "| --- | ---: | ---: | --- |",
6219
+ ...AUDIT_DIMENSIONS.map((dimension) => {
6220
+ const score = plan.scores[dimension];
6221
+ return `| ${dimension.replace(/_/g, " ")} | ${score} | 95 | ${score >= 95 ? "pass" : "gap"} |`;
6222
+ }),
6223
+ "",
6224
+ "## Self-Critique",
6225
+ "",
6226
+ ...plan.self_critique.map((item) => `- ${item.dimension}: ${item.gap}`),
6227
+ "",
6228
+ "## Findings",
6229
+ "",
6230
+ ...plan.findings.map((finding) => [
6231
+ `### ${finding.title}`,
6232
+ "",
6233
+ `- Severity: ${finding.severity}`,
6234
+ `- Evidence: ${finding.evidence}`,
6235
+ `- Recommendation: ${finding.recommendation}`,
6236
+ ""
6237
+ ].join("\n")),
6238
+ "## Extracted Founder Loop Items",
6239
+ "",
6240
+ ...plan.extracted_items.map((item) => `- ${item.type}: ${item.text} (${item.evidenceRef})`),
6241
+ "",
6242
+ "## Recommended Initiative",
6243
+ "",
6244
+ `Title: ${plan.recommended_initiative.title}`,
6245
+ "",
6246
+ plan.recommended_initiative.summary,
6247
+ "",
6248
+ "## Approved Follow-Up Candidate",
6249
+ "",
6250
+ `Title: ${plan.recommended_follow_up.title}`,
6251
+ "",
6252
+ plan.recommended_follow_up.summary,
6253
+ "",
6254
+ `Rollback: ${plan.recommended_follow_up.rollback}`,
6255
+ "",
6256
+ "## Artifact Invariant",
6257
+ "",
6258
+ "```json",
6259
+ JSON.stringify(plan.artifact_invariant, null, 2),
6260
+ "```",
6261
+ ""
6262
+ ];
6263
+ return lines.join("\n");
6264
+ }
6265
+ function parseEntityRef(payload) {
6266
+ const entity = isRecord(payload) && isRecord(payload.data) ? payload.data : payload;
6267
+ if (!isRecord(entity)) {
6268
+ throw new Error("OrgX returned an unexpected entity payload.");
6269
+ }
6270
+ const id = typeof entity.id === "string" ? entity.id : "";
6271
+ const title = typeof entity.title === "string" ? entity.title : typeof entity.name === "string" ? entity.name : "";
6272
+ if (!id || !title) {
6273
+ throw new Error("OrgX returned an incomplete entity payload.");
5676
6274
  }
6275
+ return { id, title };
5677
6276
  }
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
- }
6277
+ async function parseResponseBody5(response) {
6278
+ const text2 = await response.text();
6279
+ if (!text2) return null;
6280
+ try {
6281
+ return JSON.parse(text2);
6282
+ } catch {
6283
+ return text2;
5686
6284
  }
5687
- return null;
5688
6285
  }
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
- };
6286
+ function formatHttpError4(status, body) {
6287
+ if (typeof body === "string" && body.trim().length > 0) {
6288
+ return `HTTP ${status}: ${body}`;
5696
6289
  }
5697
- if (!options.workspace) {
5698
- return {
5699
- status: "skipped_no_workspace",
5700
- message: "People-first capture skipped \u2014 no workspace resolved."
5701
- };
6290
+ if (isRecord(body) && typeof body.error === "string" && body.error.trim().length > 0) {
6291
+ return `HTTP ${status}: ${body.error}`;
5702
6292
  }
5703
- if (options.alreadyCompleted) {
5704
- return {
5705
- status: "skipped_already_completed",
5706
- message: "People-first capture already completed for this workspace."
5707
- };
6293
+ if (isRecord(body) && isRecord(body.error) && typeof body.error.message === "string") {
6294
+ return `HTTP ${status}: ${body.error.message}`;
5708
6295
  }
5709
- const auth = await resolveOrgxAuth();
5710
- if (!auth) {
6296
+ return `HTTP ${status}`;
6297
+ }
6298
+ async function createOrgxEntity(body, options) {
6299
+ if (options.dryRun) {
5711
6300
  return {
5712
- status: "failed",
5713
- message: "People-first capture needs OrgX auth.",
5714
- error: "no_auth"
6301
+ id: `dry-run-${String(body.type ?? "entity")}`,
6302
+ title: String(body.title ?? body.name ?? "Dry-run entity")
5715
6303
  };
5716
6304
  }
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
- }
6305
+ const auth = await resolveOrgxAuth(options);
6306
+ if (!auth) {
6307
+ throw new Error("No OrgX API key configured. Run `wizard auth login` before using writeback flags.");
5726
6308
  }
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 } : {}
6309
+ const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
6310
+ body: JSON.stringify(body),
6311
+ headers: {
6312
+ Authorization: `Bearer ${auth.apiKey}`,
6313
+ "Content-Type": "application/json"
5896
6314
  },
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
6315
+ method: "POST",
6316
+ signal: AbortSignal.timeout(15e3)
5919
6317
  });
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
- };
6318
+ const responseBody = await parseResponseBody5(response);
6319
+ if (!response.ok) {
6320
+ throw new Error(`Failed to create ${String(body.type ?? "entity")}. ${formatHttpError4(response.status, responseBody)}`);
5934
6321
  }
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
- };
6322
+ return parseEntityRef(responseBody);
5958
6323
  }
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
- }
6324
+ async function createAuditArtifact(options) {
6325
+ const { initiativeId, markdown, plan } = options;
6326
+ return createOrgxEntity(
6327
+ {
6328
+ artifact_type: "shared.project_handbook",
6329
+ description: "AI-native Founder Loop self-audit generated by @useorgx/wizard audit.",
6330
+ entity_id: initiativeId,
6331
+ entity_type: "initiative",
6332
+ external_url: `orgx-wizard://audit/${plan.artifact_hash}`,
6333
+ initiative_id: initiativeId,
6334
+ metadata: {
6335
+ ...plan.artifact_invariant,
6336
+ artifact_hash: plan.artifact_hash,
6337
+ atomic_unit_type: "ai_native_self_audit",
6338
+ completion_state: "generated",
6339
+ schema_validated: true,
6340
+ scores: plan.scores
6341
+ },
6342
+ name: `AI-Native Self-Audit: ${plan.workspace.name}`,
6343
+ preview_markdown: markdown.slice(0, 8e3),
6344
+ status: "in_review",
6345
+ type: "artifact",
6346
+ workspace_id: plan.workspace.id
6347
+ },
6348
+ options
6349
+ );
6034
6350
  }
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"
6351
+ async function createInitiativeFromAuditPlan(options) {
6352
+ const { plan } = options;
6353
+ return createOrgxEntity(
6354
+ {
6355
+ metadata: {
6356
+ artifact_hash: plan.artifact_hash,
6357
+ audit_score_snapshot: plan.scores,
6358
+ source: "ai_native_self_audit",
6359
+ source_artifact_type: "ai_native_self_audit_plan"
6044
6360
  },
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
- }
6361
+ status: "active",
6362
+ summary: plan.recommended_initiative.summary,
6363
+ title: plan.recommended_initiative.title,
6364
+ type: "initiative",
6365
+ workspace_id: plan.workspace.id
6366
+ },
6367
+ options
6368
+ );
6057
6369
  }
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
- };
6370
+ async function createAuditFollowUpTask(options) {
6371
+ const { initiativeId, milestoneId, plan, workstreamId } = options;
6372
+ const resolvedWorkstreamId = workstreamId?.trim() || (await createOrgxEntity(
6373
+ {
6374
+ initiative_id: initiativeId,
6375
+ metadata: {
6376
+ artifact_hash: plan.artifact_hash,
6377
+ source: "ai_native_self_audit"
6378
+ },
6379
+ status: "active",
6380
+ summary: "Follow-up workstream created by @useorgx/wizard audit so the approved action has execution context.",
6381
+ title: "AI-native self-audit follow-up",
6382
+ type: "workstream",
6383
+ workspace_id: plan.workspace.id
6384
+ },
6385
+ options
6386
+ )).id;
6387
+ const resolvedMilestoneId = milestoneId?.trim() || (await createOrgxEntity(
6388
+ {
6389
+ initiative_id: initiativeId,
6390
+ metadata: {
6391
+ artifact_hash: plan.artifact_hash,
6392
+ source: "ai_native_self_audit"
6393
+ },
6394
+ status: "planned",
6395
+ summary: "Milestone created by @useorgx/wizard audit so the approved follow-up task has proof-chain hierarchy.",
6396
+ title: "AI-native self-audit follow-up",
6397
+ type: "milestone",
6398
+ workspace_id: plan.workspace.id,
6399
+ workstream_id: resolvedWorkstreamId
6400
+ },
6401
+ options
6402
+ )).id;
6403
+ return createOrgxEntity(
6404
+ {
6405
+ description: `${plan.recommended_follow_up.summary}
6406
+
6407
+ Rollback: ${plan.recommended_follow_up.rollback}`,
6408
+ initiative_id: initiativeId,
6409
+ milestone_id: resolvedMilestoneId,
6410
+ metadata: {
6411
+ ...plan.artifact_invariant,
6412
+ artifact_hash: plan.artifact_hash,
6413
+ source: "ai_native_self_audit",
6414
+ source_action: "approved_follow_up_writeback"
6415
+ },
6416
+ priority: "high",
6417
+ status: "todo",
6418
+ title: plan.recommended_follow_up.title,
6419
+ type: "task",
6420
+ workstream_id: resolvedWorkstreamId,
6421
+ workspace_id: plan.workspace.id
6422
+ },
6423
+ options
6424
+ );
6127
6425
  }
6128
6426
 
6129
6427
  // src/spinner.ts
@@ -6226,6 +6524,145 @@ function printPluginMutationReport(report) {
6226
6524
  );
6227
6525
  }
6228
6526
  }
6527
+ function formatScoreLine(scores) {
6528
+ return Object.entries(scores).map(([dimension, score]) => `${dimension}=${score}`).join(" ");
6529
+ }
6530
+ function readAuditInput(options, interactive) {
6531
+ if (options.input?.trim()) {
6532
+ return readFileSync3(resolve(options.input.trim()), "utf8");
6533
+ }
6534
+ if (!process.stdin.isTTY) {
6535
+ return readFileSync3(0, "utf8");
6536
+ }
6537
+ if (!interactive) {
6538
+ throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
6539
+ }
6540
+ return textPrompt({
6541
+ message: "Paste a short AI-session summary, decision log, or founder context excerpt",
6542
+ placeholder: "Decision: Founder Loop first. Artifact: proof ledger attached. Next action: ...",
6543
+ validate: (value) => value?.trim().length ? void 0 : "Audit context is required."
6544
+ }).then((value) => {
6545
+ if (clack.isCancel(value) || typeof value !== "string") {
6546
+ clack.cancel("Audit cancelled.");
6547
+ return "";
6548
+ }
6549
+ return value;
6550
+ });
6551
+ }
6552
+ function requireWriteApproval(options, interactive) {
6553
+ const wantsWrite = Boolean(options.createInitiative || options.attachToInitiative || options.writeFollowUp);
6554
+ if (!wantsWrite || options.yes || options.dryRun) return true;
6555
+ if (!interactive) {
6556
+ throw new Error("Write flags require --yes in non-interactive mode.");
6557
+ }
6558
+ return clack.confirm({
6559
+ message: "Approve OrgX writes for this audit run?"
6560
+ }).then((value) => {
6561
+ if (clack.isCancel(value) || value !== true) {
6562
+ clack.cancel("Audit writeback cancelled.");
6563
+ return false;
6564
+ }
6565
+ return true;
6566
+ });
6567
+ }
6568
+ async function resolveAuditWorkspace(options) {
6569
+ const explicitId = options.workspaceId?.trim();
6570
+ const explicitName = options.workspaceName?.trim();
6571
+ if (explicitId || explicitName) {
6572
+ return {
6573
+ id: explicitId || "manual-workspace",
6574
+ name: explicitName || explicitId || "Manual workspace"
6575
+ };
6576
+ }
6577
+ try {
6578
+ const workspace = await getCurrentWorkspace();
6579
+ if (workspace) {
6580
+ return {
6581
+ id: workspace.id,
6582
+ name: workspace.name
6583
+ };
6584
+ }
6585
+ } catch {
6586
+ }
6587
+ return {
6588
+ id: "local-workspace",
6589
+ name: "Local workspace"
6590
+ };
6591
+ }
6592
+ async function runAuditCommand(options) {
6593
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
6594
+ const text2 = (await readAuditInput(options, interactive)).trim();
6595
+ if (!text2) return;
6596
+ const workspace = await resolveAuditWorkspace(options);
6597
+ const plan = buildSelfAuditPlan({
6598
+ connectedSources: [
6599
+ options.sourceLabel?.trim() || "Manual AI-session import",
6600
+ ...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
6601
+ ],
6602
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6603
+ imports: [
6604
+ {
6605
+ sourceId: "wizard-audit-input",
6606
+ sourceLabel: options.sourceLabel?.trim() || "Wizard audit input",
6607
+ text: text2
6608
+ }
6609
+ ],
6610
+ missingSources: workspace.id === "local-workspace" ? ["OrgX workspace auth", "automatic AI-session import"] : ["automatic AI-session import"],
6611
+ workspace
6612
+ });
6613
+ const markdown = renderSelfAuditMarkdown(plan);
6614
+ const outputDir = resolve(options.outputDir?.trim() || ".orgx/audits");
6615
+ const timestamp = plan.generated_at.replace(/[:.]/g, "-");
6616
+ const jsonPath = resolve(outputDir, `ai-native-self-audit-${timestamp}.json`);
6617
+ const markdownPath = resolve(outputDir, `ai-native-self-audit-${timestamp}.md`);
6618
+ writeJsonFile(jsonPath, plan);
6619
+ writeTextFile(markdownPath, markdown);
6620
+ if (options.json) {
6621
+ console.log(JSON.stringify({ jsonPath, markdownPath, scores: plan.scores }, null, 2));
6622
+ } else {
6623
+ console.log(` ${ICON.ok} ${pc3.green("audit generated")} ${pc3.dim(markdownPath)}`);
6624
+ console.log(` ${ICON.ok} ${pc3.green("scores ")} ${pc3.dim(formatScoreLine(plan.scores))}`);
6625
+ const belowTarget = plan.self_critique.filter((item) => !item.passed);
6626
+ if (belowTarget.length === 0) {
6627
+ console.log(` ${ICON.ok} ${pc3.green("score gate ")} ${pc3.dim("all dimensions at 95+")}`);
6628
+ } else {
6629
+ console.log(` ${ICON.warn} ${pc3.yellow("score gate ")} ${pc3.dim(`${belowTarget.length} dimension${belowTarget.length === 1 ? "" : "s"} below 95`)}`);
6630
+ }
6631
+ }
6632
+ const approved = await requireWriteApproval(options, interactive);
6633
+ if (!approved) return;
6634
+ let targetInitiativeId = options.attachToInitiative?.trim() || "";
6635
+ if (options.createInitiative) {
6636
+ const initiative = await createInitiativeFromAuditPlan({
6637
+ dryRun: Boolean(options.dryRun),
6638
+ plan
6639
+ });
6640
+ targetInitiativeId = initiative.id;
6641
+ console.log(` ${ICON.ok} ${pc3.green("initiative ")} ${pc3.bold(initiative.title)} ${pc3.dim(initiative.id)}`);
6642
+ }
6643
+ if (targetInitiativeId && (options.attachToInitiative || options.createInitiative)) {
6644
+ const artifact = await createAuditArtifact({
6645
+ dryRun: Boolean(options.dryRun),
6646
+ initiativeId: targetInitiativeId,
6647
+ markdown,
6648
+ plan
6649
+ });
6650
+ console.log(` ${ICON.ok} ${pc3.green("artifact ")} ${pc3.bold(artifact.title)} ${pc3.dim(artifact.id)}`);
6651
+ }
6652
+ if (options.writeFollowUp) {
6653
+ if (!targetInitiativeId) {
6654
+ throw new Error("--write-follow-up requires --attach-to-initiative <id> or --create-initiative.");
6655
+ }
6656
+ const followUp = await createAuditFollowUpTask({
6657
+ dryRun: Boolean(options.dryRun),
6658
+ initiativeId: targetInitiativeId,
6659
+ ...options.milestoneId?.trim() ? { milestoneId: options.milestoneId.trim() } : {},
6660
+ plan,
6661
+ ...options.workstreamId?.trim() ? { workstreamId: options.workstreamId.trim() } : {}
6662
+ });
6663
+ console.log(` ${ICON.ok} ${pc3.green("follow-up ")} ${pc3.bold(followUp.title)} ${pc3.dim(followUp.id)}`);
6664
+ }
6665
+ }
6229
6666
  async function checkPluginStatusesCompact() {
6230
6667
  const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
6231
6668
  spinner.start();
@@ -6520,6 +6957,296 @@ function parseTimeoutSeconds(value) {
6520
6957
  }
6521
6958
  return parsed;
6522
6959
  }
6960
+ async function readSingleKey() {
6961
+ const stdin = process.stdin;
6962
+ if (!stdin.isTTY) return null;
6963
+ const previousRawMode = stdin.isRaw === true;
6964
+ return await new Promise((resolve2) => {
6965
+ const cleanup = (result) => {
6966
+ stdin.off("data", onData);
6967
+ if (stdin.isTTY) {
6968
+ stdin.setRawMode(previousRawMode);
6969
+ }
6970
+ stdin.pause();
6971
+ resolve2(result);
6972
+ };
6973
+ const onData = (chunk) => {
6974
+ const text2 = chunk.toString("utf8");
6975
+ const first = text2.charAt(0);
6976
+ if (first === "" || first === "") {
6977
+ cleanup(null);
6978
+ return;
6979
+ }
6980
+ cleanup(first.toLowerCase());
6981
+ };
6982
+ if (stdin.isTTY) {
6983
+ stdin.setRawMode(true);
6984
+ }
6985
+ stdin.resume();
6986
+ stdin.on("data", onData);
6987
+ });
6988
+ }
6989
+ function printIntentPreview(intent, baseUrl) {
6990
+ const render = detectRenderCapabilities();
6991
+ const agent = intent.suggested_agent_slug ?? "xandy";
6992
+ const confidence = resolveConfidenceDecimal(intent.plan_preview);
6993
+ console.log("");
6994
+ console.log(renderRoutingLine(agent, confidence, render));
6995
+ console.log("");
6996
+ console.log(renderPlanPreview(intent.plan_preview, render));
6997
+ console.log("");
6998
+ console.log(
6999
+ dim(
7000
+ ` slug ${intent.slug} handoff ${buildIntentHandoffUrl(intent.slug, { baseUrl })}`,
7001
+ render
7002
+ )
7003
+ );
7004
+ console.log("");
7005
+ console.log(renderHotkeyBar(DEFAULT_HOTKEYS, render));
7006
+ }
7007
+ async function maybeCaptureSetupIntent(input) {
7008
+ if (!input.interactive || !input.workspace) {
7009
+ return "skipped";
7010
+ }
7011
+ const prompt = await textPrompt({
7012
+ message: "In one line, what do you want OrgX to move first? (blank to skip \u2014 you can start from the dashboard)",
7013
+ placeholder: "e.g. open a warm-intro loop to 50 design-led SaaS founders",
7014
+ validate: (value) => {
7015
+ const trimmed = (value ?? "").trim();
7016
+ if (!trimmed) return void 0;
7017
+ if (trimmed.length > 4e3) return "Keep it under 4,000 characters.";
7018
+ return void 0;
7019
+ }
7020
+ });
7021
+ if (clack.isCancel(prompt)) {
7022
+ return "cancelled";
7023
+ }
7024
+ const intentText = typeof prompt === "string" ? prompt.trim() : "";
7025
+ if (!intentText) {
7026
+ return "skipped";
7027
+ }
7028
+ const spinner = createOrgxSpinner("Routing");
7029
+ spinner.start();
7030
+ let intent;
7031
+ try {
7032
+ intent = await createIntent({
7033
+ workspace_id: input.workspace.id,
7034
+ text: intentText
7035
+ });
7036
+ spinner.stop();
7037
+ } catch (error) {
7038
+ spinner.fail("Could not classify intent.");
7039
+ const message = error instanceof Error ? error.message : String(error);
7040
+ console.log(` ${ICON.warn} ${pc3.yellow("intent")} ${pc3.dim(message)}`);
7041
+ return "failed";
7042
+ }
7043
+ const auth = await resolveOrgxAuth().catch(() => null);
7044
+ const baseUrl = auth?.baseUrl ?? DEFAULT_ORGX_BASE_URL;
7045
+ printIntentPreview(intent, baseUrl);
7046
+ const approveChoice = await clack.confirm({
7047
+ message: "Approve this plan and hand off to OrgX?",
7048
+ initialValue: true
7049
+ });
7050
+ if (clack.isCancel(approveChoice)) {
7051
+ return "cancelled";
7052
+ }
7053
+ if (approveChoice) {
7054
+ const approveSpinner = createOrgxSpinner("Approving");
7055
+ approveSpinner.start();
7056
+ try {
7057
+ await updateIntent(intent.id, { status: "approved" });
7058
+ approveSpinner.stop();
7059
+ } catch (error) {
7060
+ approveSpinner.fail("Approve failed.");
7061
+ const message = error instanceof Error ? error.message : String(error);
7062
+ console.log(` ${ICON.warn} ${pc3.yellow("intent")} ${pc3.dim(message)}`);
7063
+ return "failed";
7064
+ }
7065
+ const handoff2 = buildIntentHandoffUrl(intent.slug, { baseUrl });
7066
+ console.log(` ${ICON.ok} ${pc3.green("intent")} ${pc3.dim(`approved \xB7 ${handoff2}`)}`);
7067
+ return "approved";
7068
+ }
7069
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl });
7070
+ console.log(` ${ICON.skip} ${pc3.dim(`intent pending \xB7 ${handoff}`)}`);
7071
+ return "captured";
7072
+ }
7073
+ async function runIntentCommand(options) {
7074
+ const interactive = isInteractive();
7075
+ let intentText = options.initialText?.trim() ?? "";
7076
+ if (!intentText) {
7077
+ if (!interactive) {
7078
+ console.error(
7079
+ pc3.red("Intent text is required when running non-interactively.")
7080
+ );
7081
+ console.error(
7082
+ pc3.dim(' Example: orgx-wizard intent "launch partner outreach campaign"')
7083
+ );
7084
+ process.exitCode = 1;
7085
+ return;
7086
+ }
7087
+ const prompt = await textPrompt({
7088
+ message: "What do you want to move?",
7089
+ placeholder: "e.g. launch partner outreach campaign",
7090
+ validate: (value) => {
7091
+ const trimmed = (value ?? "").trim();
7092
+ if (!trimmed) return "Intent text cannot be empty.";
7093
+ if (trimmed.length > 4e3) return "Intent text exceeds 4,000 characters.";
7094
+ return void 0;
7095
+ }
7096
+ });
7097
+ if (clack.isCancel(prompt) || typeof prompt !== "string") {
7098
+ console.log(pc3.dim("Cancelled."));
7099
+ return;
7100
+ }
7101
+ intentText = prompt.trim();
7102
+ }
7103
+ let workspaceId = options.workspaceId ?? "";
7104
+ let workspaceName = null;
7105
+ let workspaceBaseUrl = null;
7106
+ if (!workspaceId) {
7107
+ const loadLabel = "Loading current OrgX workspace";
7108
+ const spinner = interactive ? createOrgxSpinner(loadLabel) : null;
7109
+ if (spinner) spinner.start();
7110
+ else console.log(pc3.dim(`${loadLabel}...`));
7111
+ try {
7112
+ const current = await getCurrentWorkspace();
7113
+ if (!current) {
7114
+ if (spinner) spinner.fail("No OrgX workspace is configured.");
7115
+ else console.error(pc3.red("No OrgX workspace is configured."));
7116
+ console.log(
7117
+ pc3.dim(" Run `orgx-wizard workspace create <name>` or pass --workspace <id>.")
7118
+ );
7119
+ process.exitCode = 1;
7120
+ return;
7121
+ }
7122
+ workspaceId = current.id;
7123
+ workspaceName = current.name;
7124
+ if (spinner) spinner.stop();
7125
+ } catch (error) {
7126
+ if (spinner) spinner.fail("Failed to load current workspace.");
7127
+ console.error(
7128
+ pc3.red(error instanceof Error ? error.message : String(error))
7129
+ );
7130
+ process.exitCode = 1;
7131
+ return;
7132
+ }
7133
+ }
7134
+ const classifyLabel = "Routing";
7135
+ const classifySpinner = interactive ? createOrgxSpinner(classifyLabel) : null;
7136
+ if (classifySpinner) classifySpinner.start();
7137
+ else console.log(pc3.dim(`${classifyLabel}...`));
7138
+ let intent;
7139
+ try {
7140
+ intent = await createIntent({ workspace_id: workspaceId, text: intentText });
7141
+ if (classifySpinner) classifySpinner.stop();
7142
+ } catch (error) {
7143
+ if (classifySpinner) classifySpinner.fail("Classification failed.");
7144
+ console.error(
7145
+ pc3.red(error instanceof Error ? error.message : String(error))
7146
+ );
7147
+ process.exitCode = 1;
7148
+ return;
7149
+ }
7150
+ const auth = await resolveOrgxAuth().catch(() => null);
7151
+ const baseUrl = auth?.baseUrl ?? DEFAULT_ORGX_BASE_URL;
7152
+ workspaceBaseUrl = baseUrl;
7153
+ if (options.jsonOutput) {
7154
+ const payload = {
7155
+ intent,
7156
+ handoff_url: buildIntentHandoffUrl(intent.slug, { baseUrl }),
7157
+ workspace: workspaceName ? { id: workspaceId, name: workspaceName } : { id: workspaceId }
7158
+ };
7159
+ console.log(JSON.stringify(payload, null, 2));
7160
+ return;
7161
+ }
7162
+ printIntentPreview(intent, baseUrl);
7163
+ console.log("");
7164
+ if (!interactive) {
7165
+ console.log(
7166
+ pc3.dim(
7167
+ `Run \`orgx-wizard intent --workspace ${workspaceId}\` interactively to approve, edit, or archive.`
7168
+ )
7169
+ );
7170
+ return;
7171
+ }
7172
+ let action = null;
7173
+ while (action === null) {
7174
+ const key = await readSingleKey();
7175
+ if (!key) {
7176
+ action = "quit";
7177
+ break;
7178
+ }
7179
+ action = normalizeHotkey(key);
7180
+ if (action === null) {
7181
+ console.log(
7182
+ pc3.dim(` Unrecognized key. Press a, e, x, or q.`)
7183
+ );
7184
+ }
7185
+ }
7186
+ switch (action) {
7187
+ case "approve": {
7188
+ const approveLabel = "Approving";
7189
+ const spinner = interactive ? createOrgxSpinner(approveLabel) : null;
7190
+ if (spinner) spinner.start();
7191
+ try {
7192
+ await updateIntent(intent.id, { status: "approved" });
7193
+ if (spinner) spinner.stop();
7194
+ } catch (error) {
7195
+ if (spinner) spinner.fail("Approve failed.");
7196
+ console.error(
7197
+ pc3.red(error instanceof Error ? error.message : String(error))
7198
+ );
7199
+ process.exitCode = 1;
7200
+ return;
7201
+ }
7202
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl });
7203
+ console.log("");
7204
+ console.log(`${pc3.green("\u2713")} Approved \xB7 ${handoff}`);
7205
+ return;
7206
+ }
7207
+ case "edit": {
7208
+ const handoff = buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl });
7209
+ console.log("");
7210
+ console.log(`${pc3.yellow("\u270E")} Opening ${handoff}`);
7211
+ const result = openBrowser(handoff);
7212
+ if (!result.ok) {
7213
+ console.log(pc3.dim(` Could not launch browser: ${result.error}`));
7214
+ console.log(pc3.dim(` Visit ${handoff} to finish editing.`));
7215
+ }
7216
+ return;
7217
+ }
7218
+ case "archive": {
7219
+ const archiveLabel = "Archiving";
7220
+ const spinner = interactive ? createOrgxSpinner(archiveLabel) : null;
7221
+ if (spinner) spinner.start();
7222
+ try {
7223
+ await updateIntent(intent.id, { status: "archived" });
7224
+ if (spinner) spinner.stop();
7225
+ } catch (error) {
7226
+ if (spinner) spinner.fail("Archive failed.");
7227
+ console.error(
7228
+ pc3.red(error instanceof Error ? error.message : String(error))
7229
+ );
7230
+ process.exitCode = 1;
7231
+ return;
7232
+ }
7233
+ console.log("");
7234
+ console.log(`${pc3.red("\u2715")} Archived \xB7 intent will not be executed.`);
7235
+ return;
7236
+ }
7237
+ case "quit":
7238
+ default: {
7239
+ console.log("");
7240
+ console.log(pc3.dim(" Exited without saving. Intent remains pending."));
7241
+ console.log(
7242
+ pc3.dim(
7243
+ ` Resume at ${buildIntentHandoffUrl(intent.slug, { baseUrl: workspaceBaseUrl ?? baseUrl })}`
7244
+ )
7245
+ );
7246
+ return;
7247
+ }
7248
+ }
7249
+ }
6523
7250
  async function maybeConfigureOptionalWorkspaceAddOns(input) {
6524
7251
  if (!input.interactive || !input.workspace) {
6525
7252
  return "skipped";
@@ -6775,109 +7502,6 @@ async function maybeInstallOptionalCompanionPlugins(input) {
6775
7502
  ...input.telemetry ? { telemetry: input.telemetry } : {}
6776
7503
  });
6777
7504
  }
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
7505
  function printAuthStatus(status) {
6882
7506
  if (!status.configured) {
6883
7507
  console.log(` ${ICON.warn} ${pc3.yellow("no account")} run ${pc3.cyan(`${getCmd()} auth login`)} to connect`);
@@ -6956,12 +7580,12 @@ function printDoctorReport(report, assessment) {
6956
7580
  async function main() {
6957
7581
  const program = new Command();
6958
7582
  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;
7583
+ const pkgVersion = true ? "0.1.22" : void 0;
6960
7584
  program.version(pkgVersion ?? "unknown", "-V, --version");
6961
7585
  program.hook("preAction", () => {
6962
7586
  console.log(renderBanner(pkgVersion));
6963
7587
  });
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) => {
7588
+ 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
7589
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
6966
7590
  await safeTrackWizardTelemetry("wizard_started", {
6967
7591
  command: "setup",
@@ -7183,12 +7807,11 @@ async function main() {
7183
7807
  } else if (briefResult.status === "failed") {
7184
7808
  console.log(` ${ICON.warn} ${pc3.yellow("daily brief")} ${pc3.dim(briefResult.message)}`);
7185
7809
  }
7186
- const peopleFirstResult = await maybeRunPeopleFirstCapture({
7810
+ const intentResult = await maybeCaptureSetupIntent({
7187
7811
  interactive,
7188
- openInBrowser: Boolean(options.open),
7189
7812
  workspace: resolvedWorkspace
7190
7813
  });
7191
- if (peopleFirstResult === "cancelled") {
7814
+ if (intentResult === "cancelled") {
7192
7815
  return;
7193
7816
  }
7194
7817
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
@@ -7608,6 +8231,27 @@ async function main() {
7608
8231
  persistContinuityDefaults({ workspace: result.workspace });
7609
8232
  printWorkspace(result.workspace);
7610
8233
  });
8234
+ program.command("intent").description(
8235
+ "Capture an intent, preview the classified agent + plan, and approve or hand off to the browser."
8236
+ ).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) => {
8237
+ const initialText = textParts.join(" ").trim();
8238
+ const workspaceId = options.workspace?.trim() ?? "";
8239
+ await runIntentCommand({
8240
+ ...initialText ? { initialText } : {},
8241
+ ...workspaceId ? { workspaceId } : {},
8242
+ jsonOutput: Boolean(options.json)
8243
+ });
8244
+ });
8245
+ program.command("audit").description("Run the AI-native Founder Loop self-audit from pasted or file-based AI-session context.").option("--input <path>", "AI-session transcript or summary file; stdin is used when piped").option("--source-label <label>", "label for the imported AI-session source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only audits").option("--output-dir <path>", "directory for generated audit JSON and Markdown", ".orgx/audits").option("--attach-to-initiative <id>", "attach the generated audit artifact to an existing OrgX initiative").option("--create-initiative", "create an OrgX initiative from the generated audit plan").option("--write-follow-up", "create one approval-gated OrgX follow-up task from the audit recommendation").option("--workstream-id <id>", "optional workstream id for the generated follow-up task").option("--milestone-id <id>", "optional milestone id for the generated follow-up task").option("--dry-run", "exercise OrgX write paths without sending writes").option("--yes", "approve OrgX write flags in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
8246
+ await safeTrackWizardTelemetry("audit_started", {
8247
+ attach_to_initiative: Boolean(options.attachToInitiative),
8248
+ command: "audit",
8249
+ create_initiative: Boolean(options.createInitiative),
8250
+ dry_run: Boolean(options.dryRun),
8251
+ write_follow_up: Boolean(options.writeFollowUp)
8252
+ });
8253
+ await runAuditCommand(options);
8254
+ });
7611
8255
  program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
7612
8256
  const spinner = createOrgxSpinner("Running OrgX health check");
7613
8257
  spinner.start();