@useorgx/wizard 0.1.45 → 0.1.47

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 +808 -572
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +10 -9
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@
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 readFileSync7 } from "fs";
6
+ import { readFileSync as readFileSync8 } from "fs";
7
7
  import { hostname } from "os";
8
8
  import { resolve as resolve2 } from "path";
9
9
  import { Command } from "commander";
@@ -508,6 +508,31 @@ async function clearWizardAuth(authPath = ORGX_WIZARD_AUTH_PATH, options = {}) {
508
508
  return secretRemoved || fileRemoved;
509
509
  }
510
510
 
511
+ // src/lib/network.ts
512
+ var DEFAULT_REQUEST_TIMEOUT_MS = 12e3;
513
+ function isTimeoutError(error) {
514
+ if (!(error instanceof Error)) return false;
515
+ if (error.name === "TimeoutError" || error.name === "AbortError") return true;
516
+ const message = error.message.toLowerCase();
517
+ return message.includes("aborted due to timeout") || message.includes("operation was aborted");
518
+ }
519
+ async function fetchWithRetry(url, init, options = {}) {
520
+ const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
521
+ const retries = options.retries ?? 1;
522
+ let lastError;
523
+ for (let attempt = 0; attempt <= retries; attempt++) {
524
+ try {
525
+ return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
526
+ } catch (error) {
527
+ lastError = error;
528
+ if (!isTimeoutError(error) || attempt === retries) {
529
+ throw error;
530
+ }
531
+ }
532
+ }
533
+ throw lastError;
534
+ }
535
+
511
536
  // src/lib/auth.ts
512
537
  function normalizeHost(value) {
513
538
  return value.trim().toLowerCase().replace(/^\[|\]$/g, "");
@@ -652,14 +677,13 @@ async function parseResponseBody(response) {
652
677
  async function verifyOrgxAuth(auth) {
653
678
  const url = buildOrgxApiUrl("/client/sync", auth.baseUrl);
654
679
  try {
655
- const response = await fetch(url, {
680
+ const response = await fetchWithRetry(url, {
656
681
  method: "POST",
657
682
  headers: {
658
683
  Authorization: `Bearer ${auth.apiKey}`,
659
684
  "Content-Type": "application/json"
660
685
  },
661
- body: "{}",
662
- signal: AbortSignal.timeout(7e3)
686
+ body: "{}"
663
687
  });
664
688
  const data = await parseResponseBody(response);
665
689
  return {
@@ -1177,30 +1201,6 @@ function parseContinuityDefaults(value) {
1177
1201
  }
1178
1202
  return Object.keys(parsed).length > 0 ? parsed : void 0;
1179
1203
  }
1180
- function parseDemoInitiative(value) {
1181
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1182
- return void 0;
1183
- }
1184
- const record = value;
1185
- if (!isNonEmptyString2(record.id) || !isNonEmptyString2(record.title) || !isNonEmptyString2(record.liveUrl) || !isNonEmptyString2(record.createdAt)) {
1186
- return void 0;
1187
- }
1188
- return {
1189
- createdAt: record.createdAt.trim(),
1190
- ...isNonEmptyString2(record.artifactId) ? { artifactId: record.artifactId.trim() } : {},
1191
- ...isNonEmptyString2(record.artifactName) ? { artifactName: record.artifactName.trim() } : {},
1192
- ...isNonEmptyString2(record.artifactType) ? { artifactType: record.artifactType.trim() } : {},
1193
- ...isNonEmptyString2(record.artifactUrl) ? { artifactUrl: record.artifactUrl.trim() } : {},
1194
- ...isNonEmptyString2(record.decisionId) ? { decisionId: record.decisionId.trim() } : {},
1195
- ...isNonEmptyString2(record.decisionStatus) ? { decisionStatus: record.decisionStatus.trim() } : {},
1196
- ...isNonEmptyString2(record.decisionTitle) ? { decisionTitle: record.decisionTitle.trim() } : {},
1197
- id: record.id.trim(),
1198
- liveUrl: record.liveUrl.trim(),
1199
- title: record.title.trim(),
1200
- ...isNonEmptyString2(record.workspaceId) ? { workspaceId: record.workspaceId.trim() } : {},
1201
- ...isNonEmptyString2(record.workspaceName) ? { workspaceName: record.workspaceName.trim() } : {}
1202
- };
1203
- }
1204
1204
  function parseFirstValueInitiative(value) {
1205
1205
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1206
1206
  return void 0;
@@ -1376,7 +1376,7 @@ function parseDailyBriefOnboarding(value) {
1376
1376
  };
1377
1377
  }
1378
1378
  function isSetupPromptKey(value) {
1379
- return value === "first_initiative" || value === "onboarding_task" || value === "agent_roster" || value === "setup_intent";
1379
+ return value === "first_initiative" || value === "onboarding_task" || value === "agent_roster" || value === "local_skill_discovery" || value === "setup_intent";
1380
1380
  }
1381
1381
  function parseSetupPromptDecisionEntry(value) {
1382
1382
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -1421,7 +1421,6 @@ function createWizardState(now = (/* @__PURE__ */ new Date()).toISOString()) {
1421
1421
  function sanitizeWizardStateRecord(record) {
1422
1422
  const continuity = parseContinuityDefaults(record.continuity);
1423
1423
  const agentRoster = parseAgentRoster(record.agentRoster);
1424
- const demoInitiative = parseDemoInitiative(record.demoInitiative);
1425
1424
  const firstValueInitiative = parseFirstValueInitiative(record.firstValueInitiative);
1426
1425
  const onboardingTask = parseOnboardingTask(record.onboardingTask);
1427
1426
  const skillFiles = parseSkillFiles(record.skillFiles);
@@ -1434,7 +1433,6 @@ function sanitizeWizardStateRecord(record) {
1434
1433
  updatedAt: record.updatedAt.trim(),
1435
1434
  ...continuity ? { continuity } : {},
1436
1435
  ...agentRoster ? { agentRoster } : {},
1437
- ...demoInitiative ? { demoInitiative } : {},
1438
1436
  ...firstValueInitiative ? { firstValueInitiative } : {},
1439
1437
  ...onboardingTask ? { onboardingTask } : {},
1440
1438
  ...skillFiles ? { skillFiles } : {},
@@ -1457,8 +1455,6 @@ function readWizardState(statePath = ORGX_WIZARD_STATE_PATH) {
1457
1455
  if (continuity !== void 0) state.continuity = continuity;
1458
1456
  const agentRoster = parseAgentRoster(parsed.agentRoster);
1459
1457
  if (agentRoster !== void 0) state.agentRoster = agentRoster;
1460
- const demoInitiative = parseDemoInitiative(parsed.demoInitiative);
1461
- if (demoInitiative !== void 0) state.demoInitiative = demoInitiative;
1462
1458
  const firstValueInitiative = parseFirstValueInitiative(parsed.firstValueInitiative);
1463
1459
  if (firstValueInitiative !== void 0) {
1464
1460
  state.firstValueInitiative = firstValueInitiative;
@@ -1949,12 +1945,11 @@ function formatHttpError(status, body) {
1949
1945
  async function listWorkspaces(options = {}) {
1950
1946
  const auth = await requireOrgxAuth(options);
1951
1947
  const url = buildOrgxApiUrl("/entities?type=workspace&limit=100", auth.baseUrl);
1952
- const response = await fetch(url, {
1948
+ const response = await fetchWithRetry(url, {
1953
1949
  method: "GET",
1954
1950
  headers: {
1955
1951
  Authorization: `Bearer ${auth.apiKey}`
1956
- },
1957
- signal: AbortSignal.timeout(7e3)
1952
+ }
1958
1953
  });
1959
1954
  const body = await parseResponseBody2(response);
1960
1955
  if (!response.ok) {
@@ -1965,12 +1960,11 @@ async function listWorkspaces(options = {}) {
1965
1960
  async function getCurrentWorkspace(options = {}) {
1966
1961
  const auth = await requireOrgxAuth(options);
1967
1962
  const url = buildOrgxApiUrl("/v1/workspaces/current", auth.baseUrl);
1968
- const response = await fetch(url, {
1963
+ const response = await fetchWithRetry(url, {
1969
1964
  method: "GET",
1970
1965
  headers: {
1971
1966
  Authorization: `Bearer ${auth.apiKey}`
1972
- },
1973
- signal: AbortSignal.timeout(7e3)
1967
+ }
1974
1968
  });
1975
1969
  const body = await parseResponseBody2(response);
1976
1970
  if (response.ok) {
@@ -3215,8 +3209,8 @@ function encodeRepoPath2(value) {
3215
3209
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
3216
3210
  }
3217
3211
  function isLikelyRepoFilePath(path) {
3218
- const basename4 = path.split("/").pop() ?? path;
3219
- return basename4.includes(".") && !/^\.[^./]+$/.test(basename4);
3212
+ const basename5 = path.split("/").pop() ?? path;
3213
+ return basename5.includes(".") && !/^\.[^./]+$/.test(basename5);
3220
3214
  }
3221
3215
  function buildContentsUrl2(spec, path) {
3222
3216
  const encodedPath = encodeRepoPath2(path);
@@ -4594,15 +4588,17 @@ function skippedOpenClawHealth() {
4594
4588
  details: []
4595
4589
  };
4596
4590
  }
4597
- async function runDoctor() {
4591
+ async function runDoctor(options = {}) {
4598
4592
  const surfaces = listSurfaceStatuses();
4599
4593
  const openclawSurface = surfaces.find((surface) => surface.name === "openclaw");
4600
- const auth = await checkOrgxAuth();
4601
- const hostedMcp = await checkHostedMcpHealth();
4602
- const hostedMcpTool = await checkHostedMcpToolAccess();
4603
- const npmRegistry = await checkNpmRegistryHealth();
4604
- const workspace = await checkWorkspaceConnectivity();
4605
- const openclaw = openclawSurface?.detected ? await checkOpenClawHealth(openclawSurface.path) : skippedOpenClawHealth();
4594
+ const [auth, hostedMcp, hostedMcpTool, npmRegistry, workspace, openclaw] = await Promise.all([
4595
+ checkOrgxAuth(),
4596
+ checkHostedMcpHealth(),
4597
+ checkHostedMcpToolAccess(),
4598
+ checkNpmRegistryHealth(),
4599
+ options.cachedWorkspace ? Promise.resolve(options.cachedWorkspace) : checkWorkspaceConnectivity(),
4600
+ openclawSurface?.detected ? checkOpenClawHealth(openclawSurface.path) : Promise.resolve(skippedOpenClawHealth())
4601
+ ]);
4606
4602
  return { surfaces, auth, hostedMcp, hostedMcpTool, npmRegistry, workspace, openclaw };
4607
4603
  }
4608
4604
  function assessDoctorReport(report) {
@@ -4745,15 +4741,250 @@ function persistContinuityDefaults(seed = {}, statePath) {
4745
4741
  );
4746
4742
  }
4747
4743
 
4744
+ // src/lib/setup-workspace.ts
4745
+ var CREATE_WORKSPACE_VALUE = "__create_workspace__";
4746
+ var SKIP_WORKSPACE_VALUE = "__skip_workspace__";
4747
+ var REAUTH_WORKSPACE_VALUE = "__reauth_workspace__";
4748
+ function trimDescription(value) {
4749
+ const trimmed = value.trim();
4750
+ return trimmed.length > 0 ? trimmed : void 0;
4751
+ }
4752
+ function cancelResult(prompts, message = "Workspace bootstrap cancelled.") {
4753
+ prompts.cancel(message);
4754
+ return {
4755
+ message,
4756
+ status: "cancelled"
4757
+ };
4758
+ }
4759
+ function buildWorkspaceLabel(workspace, currentWorkspaceId) {
4760
+ const suffixes = [];
4761
+ if (workspace.isDefault) {
4762
+ suffixes.push("default");
4763
+ }
4764
+ if (workspace.id === currentWorkspaceId) {
4765
+ suffixes.push("current");
4766
+ }
4767
+ if (suffixes.length === 0) {
4768
+ return workspace.name;
4769
+ }
4770
+ return `${workspace.name} (${suffixes.join(", ")})`;
4771
+ }
4772
+ function buildWorkspaceSelectOptions(workspaces, currentWorkspaceId) {
4773
+ const options = workspaces.map((workspace) => ({
4774
+ value: workspace.id,
4775
+ label: buildWorkspaceLabel(workspace, currentWorkspaceId),
4776
+ ...workspace.description ? { hint: workspace.description } : {}
4777
+ }));
4778
+ options.push(
4779
+ {
4780
+ value: CREATE_WORKSPACE_VALUE,
4781
+ label: "Create a new workspace",
4782
+ hint: "Name it here and set it as the default for this machine."
4783
+ },
4784
+ {
4785
+ value: SKIP_WORKSPACE_VALUE,
4786
+ label: "Skip for now",
4787
+ hint: "Leave workspace selection unchanged and finish setup."
4788
+ }
4789
+ );
4790
+ return options;
4791
+ }
4792
+ async function promptForWorkspaceName(prompts) {
4793
+ return prompts.text({
4794
+ message: "Workspace name",
4795
+ placeholder: "Founders",
4796
+ validate(value) {
4797
+ if (!value || value.trim().length === 0) {
4798
+ return "Workspace name is required.";
4799
+ }
4800
+ return void 0;
4801
+ }
4802
+ });
4803
+ }
4804
+ async function promptForWorkspaceDescription(prompts) {
4805
+ return prompts.text({
4806
+ message: "Workspace description",
4807
+ placeholder: "Optional"
4808
+ });
4809
+ }
4810
+ async function createAndSelectWorkspace(client, prompts) {
4811
+ const name = await promptForWorkspaceName(prompts);
4812
+ if (prompts.isCancel(name)) {
4813
+ return cancelResult(prompts);
4814
+ }
4815
+ if (typeof name !== "string") {
4816
+ return cancelResult(prompts);
4817
+ }
4818
+ const description = await promptForWorkspaceDescription(prompts);
4819
+ if (prompts.isCancel(description)) {
4820
+ return cancelResult(prompts);
4821
+ }
4822
+ if (typeof description !== "string") {
4823
+ return cancelResult(prompts);
4824
+ }
4825
+ const trimmedDescription = trimDescription(description);
4826
+ const createWorkspaceInput = trimmedDescription ? { name, description: trimmedDescription } : { name };
4827
+ const created = await client.createWorkspace(createWorkspaceInput);
4828
+ if (created.isDefault) {
4829
+ return {
4830
+ created: true,
4831
+ defaultChanged: false,
4832
+ message: `Created "${created.name}" and it is already the default OrgX workspace.`,
4833
+ status: "updated",
4834
+ workspace: created
4835
+ };
4836
+ }
4837
+ const promoted = await client.setDefaultWorkspace({ id: created.id });
4838
+ return {
4839
+ created: true,
4840
+ defaultChanged: promoted.changed,
4841
+ message: `Created "${promoted.workspace.name}" and set it as the default OrgX workspace.`,
4842
+ status: "updated",
4843
+ workspace: promoted.workspace
4844
+ };
4845
+ }
4846
+ async function runWorkspaceSetup(client, prompts, options) {
4847
+ if (!options.interactive) {
4848
+ return {
4849
+ message: "Interactive workspace bootstrap skipped because this shell is not attached to a TTY.",
4850
+ status: "skipped"
4851
+ };
4852
+ }
4853
+ const workspaces = await client.listWorkspaces();
4854
+ const currentWorkspace = workspaces.length > 0 ? await client.getCurrentWorkspace().catch(() => workspaces.find((workspace) => workspace.isDefault) ?? null) : null;
4855
+ if (workspaces.length === 0) {
4856
+ const action = await prompts.select({
4857
+ message: "OrgX authenticated, but returned 0 workspaces for this key. How should setup proceed?",
4858
+ options: [
4859
+ {
4860
+ value: REAUTH_WORKSPACE_VALUE,
4861
+ label: "Re-authenticate (recommended if you expected workspaces)",
4862
+ hint: "Run `wizard auth login` to re-link via OAuth if your API key is bound to a different user identity."
4863
+ },
4864
+ {
4865
+ value: CREATE_WORKSPACE_VALUE,
4866
+ label: "Create a new workspace",
4867
+ hint: "Use this only for a truly new account \u2014 this will fork data if your key is misbound."
4868
+ },
4869
+ {
4870
+ value: SKIP_WORKSPACE_VALUE,
4871
+ label: "Skip for now",
4872
+ hint: "Finish surface setup without touching workspaces."
4873
+ }
4874
+ ],
4875
+ initialValue: REAUTH_WORKSPACE_VALUE
4876
+ });
4877
+ if (prompts.isCancel(action)) {
4878
+ return cancelResult(prompts);
4879
+ }
4880
+ if (action === REAUTH_WORKSPACE_VALUE) {
4881
+ return {
4882
+ message: "Re-authentication requested. Run `wizard auth login`, then rerun `wizard setup`.",
4883
+ status: "reauth_requested"
4884
+ };
4885
+ }
4886
+ if (action === SKIP_WORKSPACE_VALUE) {
4887
+ return {
4888
+ message: "Workspace bootstrap skipped.",
4889
+ status: "skipped"
4890
+ };
4891
+ }
4892
+ return createAndSelectWorkspace(client, prompts);
4893
+ }
4894
+ if (options.skipIfConfigured && currentWorkspace) {
4895
+ return {
4896
+ defaultChanged: false,
4897
+ message: `Using "${currentWorkspace.name}" as the default OrgX workspace.`,
4898
+ status: "unchanged",
4899
+ workspace: currentWorkspace
4900
+ };
4901
+ }
4902
+ const initialWorkspaceId = currentWorkspace?.id ?? workspaces.find((workspace) => workspace.isDefault)?.id ?? workspaces[0]?.id;
4903
+ const selected = await prompts.select({
4904
+ message: "Choose the OrgX workspace this machine should use by default.",
4905
+ options: buildWorkspaceSelectOptions(
4906
+ workspaces,
4907
+ currentWorkspace?.id ?? workspaces.find((workspace) => workspace.isDefault)?.id
4908
+ ),
4909
+ ...initialWorkspaceId ? { initialValue: initialWorkspaceId } : {}
4910
+ });
4911
+ if (prompts.isCancel(selected)) {
4912
+ return cancelResult(prompts);
4913
+ }
4914
+ if (typeof selected !== "string") {
4915
+ return cancelResult(prompts);
4916
+ }
4917
+ if (selected === SKIP_WORKSPACE_VALUE) {
4918
+ return {
4919
+ message: "Workspace bootstrap skipped.",
4920
+ status: "skipped",
4921
+ ...currentWorkspace ? { workspace: currentWorkspace } : {}
4922
+ };
4923
+ }
4924
+ if (selected === CREATE_WORKSPACE_VALUE) {
4925
+ return createAndSelectWorkspace(client, prompts);
4926
+ }
4927
+ const chosenWorkspace = workspaces.find((workspace) => workspace.id === selected);
4928
+ if (!chosenWorkspace) {
4929
+ throw new Error(`Selected workspace ${selected} is no longer available.`);
4930
+ }
4931
+ if (chosenWorkspace.isDefault) {
4932
+ return {
4933
+ defaultChanged: false,
4934
+ message: `"${chosenWorkspace.name}" is already the default OrgX workspace.`,
4935
+ status: "unchanged",
4936
+ workspace: chosenWorkspace
4937
+ };
4938
+ }
4939
+ const promoted = await client.setDefaultWorkspace({ id: chosenWorkspace.id });
4940
+ return {
4941
+ defaultChanged: promoted.changed,
4942
+ message: `Set "${promoted.workspace.name}" as the default OrgX workspace.`,
4943
+ status: promoted.changed ? "updated" : "unchanged",
4944
+ workspace: promoted.workspace
4945
+ };
4946
+ }
4947
+
4948
+ // src/lib/founder-preset.ts
4949
+ async function runFounderPreset(prompts, options) {
4950
+ const surfaceResults = await setupDetectedSurfaces();
4951
+ const pluginTargets = options.pluginTargets ?? (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
4952
+ const skillReport = await installOrgxSkills({
4953
+ pluginTargets,
4954
+ skillNames: [...DEFAULT_ORGX_SKILL_PACKS]
4955
+ });
4956
+ let workspaceSetup;
4957
+ let workspace = await getCurrentWorkspace().catch(() => null);
4958
+ if (workspace || options.interactive) {
4959
+ workspaceSetup = await runWorkspaceSetup(
4960
+ {
4961
+ createWorkspace,
4962
+ getCurrentWorkspace,
4963
+ listWorkspaces,
4964
+ setDefaultWorkspace
4965
+ },
4966
+ prompts,
4967
+ options
4968
+ );
4969
+ if (workspaceSetup.status === "cancelled") {
4970
+ return workspaceSetup;
4971
+ }
4972
+ workspace = workspaceSetup.workspace ?? workspace ?? await getCurrentWorkspace().catch(() => null);
4973
+ }
4974
+ const continuity = persistContinuityDefaults({
4975
+ statuses: listSurfaceStatuses(),
4976
+ workspace
4977
+ });
4978
+ return {
4979
+ continuity,
4980
+ skillReport,
4981
+ surfaceResults,
4982
+ workspace,
4983
+ ...workspaceSetup ? { workspaceSetup } : {}
4984
+ };
4985
+ }
4986
+
4748
4987
  // src/lib/initiatives.ts
4749
- var FOUNDER_DEMO_INITIATIVE_TITLE = "Founder Demo Initiative";
4750
- var FOUNDER_DEMO_INITIATIVE_SUMMARY = "Starter initiative created by @useorgx/wizard to validate workspace routing, live views, and the default OrgX skill pack.";
4751
- var FOUNDER_DEMO_DECISION_TITLE = "Approve founder demo workspace";
4752
- var FOUNDER_DEMO_DECISION_SUMMARY = "Initial decision created by @useorgx/wizard so the founder preset leaves the workspace with a resolved approval trail.";
4753
- var FOUNDER_DEMO_DECISION_RESOLUTION = "Approved automatically by @useorgx/wizard after the founder preset finished creating the live demo workspace.";
4754
- var FOUNDER_DEMO_ARTIFACT_NAME = "Founder Demo Live View";
4755
- var FOUNDER_DEMO_ARTIFACT_TYPE = "document";
4756
- var FOUNDER_DEMO_ARTIFACT_DESCRIPTION = "Live founder demo generated by @useorgx/wizard after workspace bootstrap completed.";
4757
4988
  var ONBOARDING_TASK_TITLE = "Complete OrgX onboarding";
4758
4989
  var ONBOARDING_TASK_SUMMARY = "Starter onboarding task created by @useorgx/wizard so the first workspace has a clear follow-up after setup.";
4759
4990
  var ONBOARDING_WORKSTREAM_TITLE = "OrgX onboarding";
@@ -4799,36 +5030,6 @@ function parseInitiative(payload) {
4799
5030
  ...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
4800
5031
  };
4801
5032
  }
4802
- function parseDecision(payload) {
4803
- const entity = extractEntity(payload);
4804
- const id = typeof entity.id === "string" ? entity.id.trim() : "";
4805
- const title = typeof entity.title === "string" ? entity.title.trim() : typeof entity.name === "string" ? entity.name.trim() : "";
4806
- if (!id || !title) {
4807
- throw new Error("OrgX returned an incomplete decision payload.");
4808
- }
4809
- return {
4810
- id,
4811
- title,
4812
- ...typeof entity.status === "string" && entity.status.trim().length > 0 ? { status: entity.status.trim() } : {},
4813
- ...typeof entity.summary === "string" && entity.summary.trim().length > 0 ? { summary: entity.summary.trim() } : {}
4814
- };
4815
- }
4816
- function parseArtifact(payload) {
4817
- const entity = extractEntity(payload);
4818
- const id = typeof entity.id === "string" ? entity.id.trim() : "";
4819
- const name = typeof entity.name === "string" ? entity.name.trim() : typeof entity.title === "string" ? entity.title.trim() : "";
4820
- const type = typeof entity.artifact_type === "string" ? entity.artifact_type.trim() : typeof entity.type === "string" ? entity.type.trim() : "";
4821
- const url = typeof entity.external_url === "string" ? entity.external_url.trim() : typeof entity.url === "string" ? entity.url.trim() : "";
4822
- if (!id || !name) {
4823
- throw new Error("OrgX returned an incomplete artifact payload.");
4824
- }
4825
- return {
4826
- id,
4827
- name,
4828
- ...type ? { type } : {},
4829
- ...url ? { url } : {}
4830
- };
4831
- }
4832
5033
  function parseTask(payload) {
4833
5034
  const entity = extractEntity(payload);
4834
5035
  const id = typeof entity.id === "string" ? entity.id.trim() : "";
@@ -4852,23 +5053,6 @@ function parseWorkstream(payload) {
4852
5053
  }
4853
5054
  return { id, title };
4854
5055
  }
4855
- function toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace) {
4856
- return {
4857
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4858
- artifactId: artifact.id,
4859
- artifactName: artifact.name,
4860
- ...artifact.type ? { artifactType: artifact.type } : {},
4861
- ...artifact.url ? { artifactUrl: artifact.url } : {},
4862
- decisionId: decision.id,
4863
- ...decision.status ? { decisionStatus: decision.status } : {},
4864
- decisionTitle: decision.title,
4865
- id: initiative.id,
4866
- liveUrl,
4867
- title: initiative.title,
4868
- workspaceId: workspace.id,
4869
- workspaceName: workspace.name
4870
- };
4871
- }
4872
5056
  function toOnboardingTaskRecord(task, workspace, options = {}) {
4873
5057
  return {
4874
5058
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4921,27 +5105,6 @@ async function createEntity(type, body, parse2, options = {}) {
4921
5105
  }
4922
5106
  return parse2(responseBody);
4923
5107
  }
4924
- async function updateEntity(type, id, body, parse2, options = {}) {
4925
- const auth = await requireOrgxAuth2(options);
4926
- const response = await fetch(buildOrgxApiUrl("/entities", auth.baseUrl), {
4927
- method: "PATCH",
4928
- headers: {
4929
- Authorization: `Bearer ${auth.apiKey}`,
4930
- "Content-Type": "application/json"
4931
- },
4932
- body: JSON.stringify({
4933
- type,
4934
- id,
4935
- ...body
4936
- }),
4937
- signal: AbortSignal.timeout(7e3)
4938
- });
4939
- const responseBody = parseResponseBody3(await response.text());
4940
- if (!response.ok) {
4941
- throw new Error(`Failed to update ${type}. ${formatHttpError2(response.status, responseBody)}`);
4942
- }
4943
- return parse2(responseBody);
4944
- }
4945
5108
  function buildLiveUrl(baseUrl, initiativeId) {
4946
5109
  const parsed = new URL(normalizeOrgxBaseUrl(baseUrl));
4947
5110
  parsed.pathname = `/live/${initiativeId}`;
@@ -4949,48 +5112,6 @@ function buildLiveUrl(baseUrl, initiativeId) {
4949
5112
  parsed.hash = "";
4950
5113
  return parsed.toString();
4951
5114
  }
4952
- async function createFounderDemoDecision(initiative, workspaceId, options = {}) {
4953
- return createEntity(
4954
- "decision",
4955
- {
4956
- initiative_id: initiative.id,
4957
- summary: FOUNDER_DEMO_DECISION_SUMMARY,
4958
- title: FOUNDER_DEMO_DECISION_TITLE,
4959
- workspace_id: workspaceId
4960
- },
4961
- parseDecision,
4962
- options
4963
- );
4964
- }
4965
- async function approveFounderDemoDecision(decisionId, options = {}) {
4966
- return updateEntity(
4967
- "decision",
4968
- decisionId,
4969
- {
4970
- resolution_summary: FOUNDER_DEMO_DECISION_RESOLUTION,
4971
- status: "approved"
4972
- },
4973
- parseDecision,
4974
- options
4975
- );
4976
- }
4977
- async function createFounderDemoArtifact(initiative, liveUrl, workspaceId, options = {}) {
4978
- return createEntity(
4979
- "artifact",
4980
- {
4981
- artifact_type: FOUNDER_DEMO_ARTIFACT_TYPE,
4982
- description: FOUNDER_DEMO_ARTIFACT_DESCRIPTION,
4983
- entity_id: initiative.id,
4984
- entity_type: "initiative",
4985
- external_url: liveUrl,
4986
- initiative_id: initiative.id,
4987
- name: FOUNDER_DEMO_ARTIFACT_NAME,
4988
- workspace_id: workspaceId
4989
- },
4990
- parseArtifact,
4991
- options
4992
- );
4993
- }
4994
5115
  async function createInitiative(input, options = {}) {
4995
5116
  const title = input.title.trim();
4996
5117
  if (!title) {
@@ -5008,52 +5129,6 @@ async function createInitiative(input, options = {}) {
5008
5129
  options
5009
5130
  );
5010
5131
  }
5011
- async function ensureFounderDemoInitiative(workspace, options = {}) {
5012
- const existingRecord = readWizardState(options.statePath)?.demoInitiative;
5013
- const matchingRecord = existingRecord?.workspaceId === workspace.id ? existingRecord : void 0;
5014
- const auth = await requireOrgxAuth2(options);
5015
- const initiative = matchingRecord ? {
5016
- id: matchingRecord.id,
5017
- title: matchingRecord.title
5018
- } : await createInitiative(
5019
- {
5020
- title: FOUNDER_DEMO_INITIATIVE_TITLE,
5021
- summary: FOUNDER_DEMO_INITIATIVE_SUMMARY,
5022
- workspaceId: workspace.id
5023
- },
5024
- options
5025
- );
5026
- const liveUrl = matchingRecord?.liveUrl || buildLiveUrl(auth.baseUrl, initiative.id);
5027
- const decision = matchingRecord?.decisionId ? matchingRecord.decisionStatus === "approved" && matchingRecord.decisionTitle ? {
5028
- id: matchingRecord.decisionId,
5029
- status: matchingRecord.decisionStatus,
5030
- title: matchingRecord.decisionTitle
5031
- } : await approveFounderDemoDecision(matchingRecord.decisionId, options) : await approveFounderDemoDecision(
5032
- (await createFounderDemoDecision(initiative, workspace.id, options)).id,
5033
- options
5034
- );
5035
- const artifact = matchingRecord?.artifactId && matchingRecord.artifactName ? {
5036
- id: matchingRecord.artifactId,
5037
- name: matchingRecord.artifactName,
5038
- ...matchingRecord.artifactType ? { type: matchingRecord.artifactType } : {},
5039
- ...matchingRecord.artifactUrl ? { url: matchingRecord.artifactUrl } : {}
5040
- } : await createFounderDemoArtifact(initiative, liveUrl, workspace.id, options);
5041
- const record = toDemoInitiativeRecord(artifact, decision, initiative, liveUrl, workspace);
5042
- updateWizardState(
5043
- (current) => ({
5044
- ...current,
5045
- demoInitiative: record
5046
- }),
5047
- options.statePath
5048
- );
5049
- return {
5050
- artifact,
5051
- created: !matchingRecord,
5052
- decision,
5053
- initiative,
5054
- liveUrl
5055
- };
5056
- }
5057
5132
  async function ensureFirstValueInitiative(workspace, options = {}) {
5058
5133
  const existingRecord = readWizardState(options.statePath)?.firstValueInitiative;
5059
5134
  const matchingRecord = existingRecord?.workspaceId === workspace.id ? existingRecord : void 0;
@@ -5143,253 +5218,206 @@ async function ensureOnboardingTask(workspace, options = {}) {
5143
5218
  );
5144
5219
  return task;
5145
5220
  }
5146
-
5147
- // src/lib/setup-workspace.ts
5148
- var CREATE_WORKSPACE_VALUE = "__create_workspace__";
5149
- var SKIP_WORKSPACE_VALUE = "__skip_workspace__";
5150
- var REAUTH_WORKSPACE_VALUE = "__reauth_workspace__";
5151
- function trimDescription(value) {
5152
- const trimmed = value.trim();
5153
- return trimmed.length > 0 ? trimmed : void 0;
5154
- }
5155
- function cancelResult(prompts, message = "Workspace bootstrap cancelled.") {
5156
- prompts.cancel(message);
5157
- return {
5158
- message,
5159
- status: "cancelled"
5160
- };
5161
- }
5162
- function buildWorkspaceLabel(workspace, currentWorkspaceId) {
5163
- const suffixes = [];
5164
- if (workspace.isDefault) {
5165
- suffixes.push("default");
5166
- }
5167
- if (workspace.id === currentWorkspaceId) {
5168
- suffixes.push("current");
5169
- }
5170
- if (suffixes.length === 0) {
5171
- return workspace.name;
5172
- }
5173
- return `${workspace.name} (${suffixes.join(", ")})`;
5174
- }
5175
- function buildWorkspaceSelectOptions(workspaces, currentWorkspaceId) {
5176
- const options = workspaces.map((workspace) => ({
5177
- value: workspace.id,
5178
- label: buildWorkspaceLabel(workspace, currentWorkspaceId),
5179
- ...workspace.description ? { hint: workspace.description } : {}
5180
- }));
5181
- options.push(
5182
- {
5183
- value: CREATE_WORKSPACE_VALUE,
5184
- label: "Create a new workspace",
5185
- hint: "Name it here and set it as the default for this machine."
5186
- },
5187
- {
5188
- value: SKIP_WORKSPACE_VALUE,
5189
- label: "Skip for now",
5190
- hint: "Leave workspace selection unchanged and finish setup."
5191
- }
5192
- );
5193
- return options;
5194
- }
5195
- async function promptForWorkspaceName(prompts) {
5196
- return prompts.text({
5197
- message: "Workspace name",
5198
- placeholder: "Founders",
5199
- validate(value) {
5200
- if (!value || value.trim().length === 0) {
5201
- return "Workspace name is required.";
5202
- }
5203
- return void 0;
5204
- }
5205
- });
5206
- }
5207
- async function promptForWorkspaceDescription(prompts) {
5208
- return prompts.text({
5209
- message: "Workspace description",
5210
- placeholder: "Optional"
5211
- });
5212
- }
5213
- async function createAndSelectWorkspace(client, prompts) {
5214
- const name = await promptForWorkspaceName(prompts);
5215
- if (prompts.isCancel(name)) {
5216
- return cancelResult(prompts);
5217
- }
5218
- if (typeof name !== "string") {
5219
- return cancelResult(prompts);
5220
- }
5221
- const description = await promptForWorkspaceDescription(prompts);
5222
- if (prompts.isCancel(description)) {
5223
- return cancelResult(prompts);
5224
- }
5225
- if (typeof description !== "string") {
5226
- return cancelResult(prompts);
5227
- }
5228
- const trimmedDescription = trimDescription(description);
5229
- const createWorkspaceInput = trimmedDescription ? { name, description: trimmedDescription } : { name };
5230
- const created = await client.createWorkspace(createWorkspaceInput);
5231
- if (created.isDefault) {
5232
- return {
5233
- created: true,
5234
- defaultChanged: false,
5235
- message: `Created "${created.name}" and it is already the default OrgX workspace.`,
5236
- status: "updated",
5237
- workspace: created
5238
- };
5239
- }
5240
- const promoted = await client.setDefaultWorkspace({ id: created.id });
5241
- return {
5242
- created: true,
5243
- defaultChanged: promoted.changed,
5244
- message: `Created "${promoted.workspace.name}" and set it as the default OrgX workspace.`,
5245
- status: "updated",
5246
- workspace: promoted.workspace
5247
- };
5248
- }
5249
- async function runWorkspaceSetup(client, prompts, options) {
5250
- if (!options.interactive) {
5251
- return {
5252
- message: "Interactive workspace bootstrap skipped because this shell is not attached to a TTY.",
5253
- status: "skipped"
5254
- };
5255
- }
5256
- const workspaces = await client.listWorkspaces();
5257
- const currentWorkspace = workspaces.length > 0 ? await client.getCurrentWorkspace().catch(() => workspaces.find((workspace) => workspace.isDefault) ?? null) : null;
5258
- if (workspaces.length === 0) {
5259
- const action = await prompts.select({
5260
- message: "OrgX authenticated, but returned 0 workspaces for this key. How should setup proceed?",
5261
- options: [
5262
- {
5263
- value: REAUTH_WORKSPACE_VALUE,
5264
- label: "Re-authenticate (recommended if you expected workspaces)",
5265
- hint: "Run `wizard auth login` to re-link via OAuth if your API key is bound to a different user identity."
5266
- },
5267
- {
5268
- value: CREATE_WORKSPACE_VALUE,
5269
- label: "Create a new workspace",
5270
- hint: "Use this only for a truly new account \u2014 this will fork data if your key is misbound."
5271
- },
5272
- {
5273
- value: SKIP_WORKSPACE_VALUE,
5274
- label: "Skip for now",
5275
- hint: "Finish surface setup without touching workspaces."
5276
- }
5277
- ],
5278
- initialValue: REAUTH_WORKSPACE_VALUE
5279
- });
5280
- if (prompts.isCancel(action)) {
5281
- return cancelResult(prompts);
5282
- }
5283
- if (action === REAUTH_WORKSPACE_VALUE) {
5284
- return {
5285
- message: "Re-authentication requested. Run `wizard auth login`, then rerun `wizard setup`.",
5286
- status: "reauth_requested"
5287
- };
5221
+
5222
+ // src/lib/local-skill-discovery.ts
5223
+ import { createHash as createHash3 } from "crypto";
5224
+ import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
5225
+ import { basename as basename2, join as join4, relative as relative2 } from "path";
5226
+ var DEFAULT_MAX_BYTES = 48e3;
5227
+ var DEFAULT_LIMIT = 12;
5228
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", ".next", ".turbo", "build", "dist", "node_modules"]);
5229
+ var LOCAL_SKILL_SOURCES = ["opencode", "claude", "codex", "agents", "workspace"];
5230
+ var DOMAIN_KEYWORDS = [
5231
+ { domain: "engineering", pattern: /\b(eval|benchmark|test|ci|code|github|repo|runtime|api|llm|model)\b/i },
5232
+ { domain: "product", pattern: /\b(customer|workflow|roadmap|prd|user|adoption|value|requirements?)\b/i },
5233
+ { domain: "operations", pattern: /\b(runbook|incident|sla|dashboard|superset|metric|monitor|ops)\b/i },
5234
+ { domain: "design", pattern: /\b(ui|ux|design|accessibility|component|visual)\b/i },
5235
+ { domain: "marketing", pattern: /\b(launch|campaign|positioning|gtm|content|story)\b/i },
5236
+ { domain: "sales", pattern: /\b(deal|pipeline|prospect|meddic|outreach|buyer)\b/i }
5237
+ ];
5238
+ function hash(value, length = 10) {
5239
+ return createHash3("sha256").update(value).digest("hex").slice(0, length);
5240
+ }
5241
+ function safeStat(path) {
5242
+ try {
5243
+ return statSync3(path);
5244
+ } catch {
5245
+ return null;
5246
+ }
5247
+ }
5248
+ function defaultRoots(input) {
5249
+ return {
5250
+ agents: [join4(input.home, ".agents", "skills")],
5251
+ claude: [join4(input.home, ".claude", "skills"), join4(input.cwd, ".claude", "skills")],
5252
+ codex: [join4(input.home, ".codex", "skills"), join4(input.cwd, ".codex", "skills")],
5253
+ opencode: [
5254
+ join4(input.home, ".opencode", "skills"),
5255
+ join4(input.home, ".config", "opencode", "skills"),
5256
+ join4(input.home, "Library", "Application Support", "opencode", "skills"),
5257
+ join4(input.cwd, ".opencode", "skills")
5258
+ ],
5259
+ workspace: [
5260
+ join4(input.cwd, "skills"),
5261
+ join4(input.cwd, ".agents", "skills"),
5262
+ join4(input.cwd, ".orgx", "skills")
5263
+ ]
5264
+ };
5265
+ }
5266
+ function walkSkillFiles(root, maxFiles = 200) {
5267
+ const rootStats = safeStat(root);
5268
+ if (!rootStats) return [];
5269
+ if (rootStats.isFile()) return [root];
5270
+ if (!rootStats.isDirectory()) return [];
5271
+ const files = [];
5272
+ const stack = [root];
5273
+ while (stack.length > 0 && files.length < maxFiles) {
5274
+ const current = stack.pop();
5275
+ if (!current) continue;
5276
+ let entries;
5277
+ try {
5278
+ entries = readdirSync3(current, { withFileTypes: true });
5279
+ } catch {
5280
+ continue;
5288
5281
  }
5289
- if (action === SKIP_WORKSPACE_VALUE) {
5290
- return {
5291
- message: "Workspace bootstrap skipped.",
5292
- status: "skipped"
5293
- };
5282
+ for (const entry of entries) {
5283
+ if (IGNORED_DIRS.has(entry.name)) continue;
5284
+ const path = join4(current, entry.name);
5285
+ if (entry.isDirectory()) {
5286
+ stack.push(path);
5287
+ } else if (entry.isFile() && /\.(md|mdc|txt)$/i.test(entry.name)) {
5288
+ files.push(path);
5289
+ }
5294
5290
  }
5295
- return createAndSelectWorkspace(client, prompts);
5296
- }
5297
- if (options.skipIfConfigured && currentWorkspace) {
5298
- return {
5299
- defaultChanged: false,
5300
- message: `Using "${currentWorkspace.name}" as the default OrgX workspace.`,
5301
- status: "unchanged",
5302
- workspace: currentWorkspace
5303
- };
5304
- }
5305
- const initialWorkspaceId = currentWorkspace?.id ?? workspaces.find((workspace) => workspace.isDefault)?.id ?? workspaces[0]?.id;
5306
- const selected = await prompts.select({
5307
- message: "Choose the OrgX workspace this machine should use by default.",
5308
- options: buildWorkspaceSelectOptions(
5309
- workspaces,
5310
- currentWorkspace?.id ?? workspaces.find((workspace) => workspace.isDefault)?.id
5311
- ),
5312
- ...initialWorkspaceId ? { initialValue: initialWorkspaceId } : {}
5313
- });
5314
- if (prompts.isCancel(selected)) {
5315
- return cancelResult(prompts);
5316
5291
  }
5317
- if (typeof selected !== "string") {
5318
- return cancelResult(prompts);
5292
+ return files;
5293
+ }
5294
+ function readWindow(path, maxBytes) {
5295
+ const stats = safeStat(path);
5296
+ if (!stats?.isFile() || stats.size === 0) return null;
5297
+ try {
5298
+ const text2 = readFileSync3(path, "utf8");
5299
+ return text2.slice(0, maxBytes);
5300
+ } catch {
5301
+ return null;
5319
5302
  }
5320
- if (selected === SKIP_WORKSPACE_VALUE) {
5321
- return {
5322
- message: "Workspace bootstrap skipped.",
5323
- status: "skipped",
5324
- ...currentWorkspace ? { workspace: currentWorkspace } : {}
5325
- };
5303
+ }
5304
+ function titleFrom(path, text2) {
5305
+ const heading = text2.match(/^#\s+(.+)$/m)?.[1]?.trim();
5306
+ if (heading) return heading.slice(0, 120);
5307
+ return basename2(path).replace(/\.(md|mdc|txt)$/i, "").replace(/[-_]+/g, " ");
5308
+ }
5309
+ function snippetFrom(text2) {
5310
+ const lines = text2.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("---") && !/^#+\s/.test(line));
5311
+ return lines.slice(0, 4).join(" ").replace(/\s+/g, " ").slice(0, 420);
5312
+ }
5313
+ function tokenize(value) {
5314
+ return value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 3);
5315
+ }
5316
+ function scoreCandidate(text2, context, source) {
5317
+ const haystack = text2.toLowerCase();
5318
+ const contextTokens = [...new Set(tokenize(context))];
5319
+ const contextHits = contextTokens.filter((token) => haystack.includes(token)).length;
5320
+ const explicitSkillSignal = /\b(skill|agent|workflow|instruction|rule|prompt|playbook)\b/i.test(text2) ? 2 : 0;
5321
+ const sourceBoost = source === "opencode" ? 2 : source === "workspace" ? 1 : 0;
5322
+ const score = contextHits * 3 + explicitSkillSignal + sourceBoost;
5323
+ const reasonParts = [
5324
+ contextHits > 0 ? `${contextHits} context match${contextHits === 1 ? "" : "es"}` : "local skill file",
5325
+ sourceBoost > 0 ? `${source} source` : ""
5326
+ ].filter(Boolean);
5327
+ return { reason: reasonParts.join("; "), score };
5328
+ }
5329
+ function inferDomains(text2, context) {
5330
+ const joined = `${text2}
5331
+ ${context}`;
5332
+ const domains = DOMAIN_KEYWORDS.filter((entry) => entry.pattern.test(joined)).map((entry) => entry.domain);
5333
+ return domains.length > 0 ? [...new Set(domains)] : ["orchestrator"];
5334
+ }
5335
+ function parseLocalSkillSources(value) {
5336
+ if (!value?.trim() || value.trim().toLowerCase() === "all") {
5337
+ return [...LOCAL_SKILL_SOURCES];
5326
5338
  }
5327
- if (selected === CREATE_WORKSPACE_VALUE) {
5328
- return createAndSelectWorkspace(client, prompts);
5339
+ const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
5340
+ const invalid = requested.filter((source) => !LOCAL_SKILL_SOURCES.includes(source));
5341
+ if (invalid.length > 0) {
5342
+ throw new Error(`Unsupported local skill source: ${invalid.join(", ")}. Use ${LOCAL_SKILL_SOURCES.join(", ")}, or all.`);
5329
5343
  }
5330
- const chosenWorkspace = workspaces.find((workspace) => workspace.id === selected);
5331
- if (!chosenWorkspace) {
5332
- throw new Error(`Selected workspace ${selected} is no longer available.`);
5344
+ return [...new Set(requested)];
5345
+ }
5346
+ function discoverLocalSkills(options = {}) {
5347
+ const cwd = options.cwd ?? process.cwd();
5348
+ const home = options.home ?? process.env.HOME ?? "";
5349
+ const sources = options.sources ?? [...LOCAL_SKILL_SOURCES];
5350
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
5351
+ const context = options.context?.trim() ?? "";
5352
+ const roots = defaultRoots({ cwd, home });
5353
+ for (const [source, overrides] of Object.entries(options.roots ?? {})) {
5354
+ roots[source] = overrides;
5333
5355
  }
5334
- if (chosenWorkspace.isDefault) {
5335
- return {
5336
- defaultChanged: false,
5337
- message: `"${chosenWorkspace.name}" is already the default OrgX workspace.`,
5338
- status: "unchanged",
5339
- workspace: chosenWorkspace
5340
- };
5356
+ const seen = /* @__PURE__ */ new Set();
5357
+ const candidates = [];
5358
+ for (const source of sources) {
5359
+ for (const root of roots[source] ?? []) {
5360
+ for (const path of walkSkillFiles(root)) {
5361
+ if (seen.has(path)) continue;
5362
+ seen.add(path);
5363
+ const text2 = readWindow(path, maxBytes);
5364
+ if (!text2 || /ORGX SKILL COMPOSED v1/.test(text2)) continue;
5365
+ const title = titleFrom(path, text2);
5366
+ const snippet = snippetFrom(text2);
5367
+ const scored = scoreCandidate(`${title}
5368
+ ${snippet}
5369
+ ${text2}`, context, source);
5370
+ candidates.push({
5371
+ agentDomains: inferDomains(`${title}
5372
+ ${snippet}`, context),
5373
+ id: `${source}-${hash(path)}`,
5374
+ path,
5375
+ reason: scored.reason,
5376
+ score: scored.score,
5377
+ snippet,
5378
+ source,
5379
+ title
5380
+ });
5381
+ }
5382
+ }
5341
5383
  }
5342
- const promoted = await client.setDefaultWorkspace({ id: chosenWorkspace.id });
5343
- return {
5344
- defaultChanged: promoted.changed,
5345
- message: `Set "${promoted.workspace.name}" as the default OrgX workspace.`,
5346
- status: promoted.changed ? "updated" : "unchanged",
5347
- workspace: promoted.workspace
5348
- };
5384
+ return candidates.sort((left, right) => right.score - left.score || left.title.localeCompare(right.title)).slice(0, Math.max(1, options.limit ?? DEFAULT_LIMIT));
5349
5385
  }
5350
-
5351
- // src/lib/founder-preset.ts
5352
- async function runFounderPreset(prompts, options) {
5353
- const surfaceResults = await setupDetectedSurfaces();
5354
- const pluginTargets = options.pluginTargets ?? (await listOrgxPluginStatuses()).filter((status) => status.installed).map((status) => status.target);
5355
- const skillReport = await installOrgxSkills({
5356
- pluginTargets,
5357
- skillNames: [...DEFAULT_ORGX_SKILL_PACKS]
5358
- });
5359
- let workspaceSetup;
5360
- let workspace = await getCurrentWorkspace().catch(() => null);
5361
- if (workspace || options.interactive) {
5362
- workspaceSetup = await runWorkspaceSetup(
5363
- {
5364
- createWorkspace,
5365
- getCurrentWorkspace,
5366
- listWorkspaces,
5367
- setDefaultWorkspace
5368
- },
5369
- prompts,
5370
- options
5371
- );
5372
- if (workspaceSetup.status === "cancelled") {
5373
- return workspaceSetup;
5386
+ function selectLocalSkillCandidates(candidates, selection) {
5387
+ const wanted = selection.split(",").map((item) => item.trim()).filter(Boolean);
5388
+ const selected = [];
5389
+ for (const item of wanted) {
5390
+ const byIndex = /^\d+$/.test(item) ? candidates[Number(item) - 1] : void 0;
5391
+ const byId = candidates.find((candidate) => candidate.id === item);
5392
+ const match = byIndex ?? byId;
5393
+ if (!match) {
5394
+ throw new Error(`No local skill candidate matched '${item}'.`);
5374
5395
  }
5375
- workspace = workspaceSetup.workspace ?? workspace ?? await getCurrentWorkspace().catch(() => null);
5396
+ selected.push(match);
5376
5397
  }
5377
- const continuity = persistContinuityDefaults({
5378
- statuses: listSurfaceStatuses(),
5379
- workspace
5380
- });
5381
- let demoInitiative;
5382
- if (workspace) {
5383
- demoInitiative = await ensureFounderDemoInitiative(workspace);
5398
+ return [...new Map(selected.map((candidate) => [candidate.id, candidate])).values()];
5399
+ }
5400
+ function buildLocalSkillExtensionContent(candidates, context) {
5401
+ const lines = [
5402
+ "# Local Skill Preferences",
5403
+ "",
5404
+ "Use these opt-in local preferences when they are relevant to the current OrgX initiative. Do not override explicit user instructions or repo guardrails."
5405
+ ];
5406
+ const trimmedContext = context?.trim();
5407
+ if (trimmedContext) {
5408
+ lines.push("", `Context: ${trimmedContext}`);
5384
5409
  }
5385
- return {
5386
- continuity,
5387
- ...demoInitiative ? { demoInitiative } : {},
5388
- skillReport,
5389
- surfaceResults,
5390
- workspace,
5391
- ...workspaceSetup ? { workspaceSetup } : {}
5392
- };
5410
+ for (const candidate of candidates) {
5411
+ lines.push(
5412
+ "",
5413
+ `## ${candidate.title}`,
5414
+ "",
5415
+ `- Source: ${candidate.source} (${relative2(process.cwd(), candidate.path)})`,
5416
+ `- Suggested agents: ${candidate.agentDomains.join(", ")}`,
5417
+ `- Preserve: ${candidate.snippet || "local workflow preference from this skill file."}`
5418
+ );
5419
+ }
5420
+ return lines.join("\n");
5393
5421
  }
5394
5422
 
5395
5423
  // src/lib/mutation-output.ts
@@ -5406,6 +5434,9 @@ var HOSTED_MCP_OUTAGE_TITLE = "Hosted OrgX MCP is unreachable.";
5406
5434
  function getSetupVerificationHeadline(summary) {
5407
5435
  switch (summary.status) {
5408
5436
  case "degraded":
5437
+ if (summary.transientTimeouts) {
5438
+ return "OrgX servers were slow to respond \u2014 setup applied, but the final health check timed out. Re-run `wizard doctor` in a moment to confirm.";
5439
+ }
5409
5440
  return "Hosted OrgX MCP is down; local setup can still continue.";
5410
5441
  case "error":
5411
5442
  return "Issues detected";
@@ -5415,18 +5446,50 @@ function getSetupVerificationHeadline(summary) {
5415
5446
  return "All systems ready.";
5416
5447
  }
5417
5448
  }
5418
- function summarizeSetupVerification(assessment) {
5449
+ function isTimeoutErrorString(value) {
5450
+ if (!value) return false;
5451
+ return isTimeoutError(new Error(value));
5452
+ }
5453
+ function reportErrorsAreOnlyTimeouts(report) {
5454
+ if (!report) return false;
5455
+ const authTimedOut = !report.auth.ok && !report.auth.skipped && isTimeoutErrorString(report.auth.error);
5456
+ const workspaceTimedOut = !report.workspace.ok && !report.workspace.skipped && isTimeoutErrorString(report.workspace.error);
5457
+ const reachableServicesOk = (report.hostedMcp.skipped || report.hostedMcp.ok) && (report.openclaw.skipped || report.openclaw.ok);
5458
+ return reachableServicesOk && (authTimedOut || workspaceTimedOut);
5459
+ }
5460
+ function summarizeSetupVerification(assessment, report) {
5419
5461
  const errors = assessment.issues.filter((issue) => issue.level === "error");
5420
5462
  if (errors.length === 0) {
5421
5463
  return {
5422
5464
  hostedMcpDegraded: false,
5465
+ transientTimeouts: false,
5423
5466
  status: assessment.issues.length > 0 ? "warning" : "ok"
5424
5467
  };
5425
5468
  }
5426
5469
  const hostedMcpDegraded = errors.every((issue) => issue.title === HOSTED_MCP_OUTAGE_TITLE);
5470
+ if (hostedMcpDegraded) {
5471
+ return {
5472
+ hostedMcpDegraded: true,
5473
+ transientTimeouts: false,
5474
+ status: "degraded"
5475
+ };
5476
+ }
5477
+ const authOrWorkspaceTitles = /* @__PURE__ */ new Set([
5478
+ "OrgX user auth could not be verified.",
5479
+ "Current workspace lookup failed."
5480
+ ]);
5481
+ const onlyAuthOrWorkspaceErrors = errors.every((issue) => authOrWorkspaceTitles.has(issue.title));
5482
+ if (onlyAuthOrWorkspaceErrors && reportErrorsAreOnlyTimeouts(report)) {
5483
+ return {
5484
+ hostedMcpDegraded: false,
5485
+ transientTimeouts: true,
5486
+ status: "degraded"
5487
+ };
5488
+ }
5427
5489
  return {
5428
- hostedMcpDegraded,
5429
- status: hostedMcpDegraded ? "degraded" : "error"
5490
+ hostedMcpDegraded: false,
5491
+ transientTimeouts: false,
5492
+ status: "error"
5430
5493
  };
5431
5494
  }
5432
5495
 
@@ -5468,16 +5531,6 @@ function buildAgentRosterTelemetryProperties(input, base = {}) {
5468
5531
  base
5469
5532
  );
5470
5533
  }
5471
- function buildFounderDemoTelemetryProperties(result, base = {}) {
5472
- return withBaseProperties(
5473
- {
5474
- created: result.created,
5475
- decision_status: result.decision.status ?? "unknown",
5476
- has_artifact_url: Boolean(result.artifact.url)
5477
- },
5478
- base
5479
- );
5480
- }
5481
5534
  function buildFirstValueInitiativeTelemetryProperties(result, base = {}) {
5482
5535
  return withBaseProperties(
5483
5536
  {
@@ -5500,6 +5553,7 @@ function buildDoctorTelemetryProperties(report, assessment, verification, base =
5500
5553
  hosted_mcp_degraded: verification.hostedMcpDegraded,
5501
5554
  hosted_mcp_ok: report.hostedMcp.ok,
5502
5555
  hosted_mcp_tool_ok: report.hostedMcpTool.ok,
5556
+ transient_timeouts: verification.transientTimeouts,
5503
5557
  issue_count: assessment.issues.length,
5504
5558
  openclaw_available: !report.openclaw.skipped,
5505
5559
  openclaw_ok: report.openclaw.ok,
@@ -5913,6 +5967,40 @@ function normalizeHotkey(input) {
5913
5967
  }
5914
5968
  }
5915
5969
 
5970
+ // src/lib/setup-profiles.ts
5971
+ var SETUP_PROFILES = {
5972
+ "local-ai-workflow": {
5973
+ id: "local-ai-workflow",
5974
+ label: "Local AI workflow",
5975
+ firstInitiativeTitle: "Make local AI work visible and shippable",
5976
+ firstInitiativeSummary: [
5977
+ "First OrgX initiative tailored for a local AI-assisted workflow.",
5978
+ "Use local AI-session evidence, Git/GitHub proof, and existing project context to create a live work graph, a concrete onboarding task, and a path to cloud execution once GitHub is connected."
5979
+ ].join(" "),
5980
+ handoffPrompt: "Use OrgX to continue this local AI-workflow initiative. Start with the onboarding task, inspect local AI-client and GitHub proof, preserve selected local skills, and show the next action plus the cloud handoff once GitHub is connected.",
5981
+ localProofCommand: "orgx-wizard sessions reconcile --from opencode,github --public-share --yes",
5982
+ skillDiscoveryCommand: `orgx-wizard skills discover-local --from opencode,claude,codex,workspace --context "Describe this person's domain, tools, and workflow"`
5983
+ }
5984
+ };
5985
+ function resolveSetupProfile(id) {
5986
+ const normalized = id?.trim().toLowerCase();
5987
+ if (!normalized) return null;
5988
+ return SETUP_PROFILES[normalized] ?? null;
5989
+ }
5990
+ function supportedSetupProfileIds() {
5991
+ return Object.keys(SETUP_PROFILES).sort();
5992
+ }
5993
+ function buildProfileSummary(profile, context) {
5994
+ const trimmedContext = context?.trim();
5995
+ if (!trimmedContext) return profile.firstInitiativeSummary;
5996
+ return `${profile.firstInitiativeSummary} Context: ${trimmedContext}`;
5997
+ }
5998
+ function buildProfileHandoffPrompt(profile, fallbackPrompt, context) {
5999
+ if (!profile) return fallbackPrompt;
6000
+ const trimmedContext = context?.trim();
6001
+ return trimmedContext ? `${profile.handoffPrompt} Context: ${trimmedContext}` : profile.handoffPrompt;
6002
+ }
6003
+
5916
6004
  // src/lib/daily-brief-onboarding.ts
5917
6005
  function extractErrorHint(body) {
5918
6006
  if (!body) return null;
@@ -6222,8 +6310,8 @@ async function fetchOnboardingState(auth) {
6222
6310
  }
6223
6311
 
6224
6312
  // src/lib/ai-session-import.ts
6225
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
6226
- import { basename as basename2, join as join4, relative as relative2 } from "path";
6313
+ import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
6314
+ import { basename as basename3, join as join5, relative as relative3 } from "path";
6227
6315
  var AI_SESSION_SOURCES = ["codex", "claude"];
6228
6316
  var DEFAULT_LIMIT_PER_SOURCE = 3;
6229
6317
  var DEFAULT_SINCE_DAYS = 30;
@@ -6376,7 +6464,7 @@ function keepAuditRelevantLines(text2) {
6376
6464
  return text2.split(/\r?\n/).map((line) => normalizeAuditRelevantLine(line)).filter((line) => Boolean(line));
6377
6465
  }
6378
6466
  function collectJsonlFiles(root, source) {
6379
- if (!existsSync5(root)) return [];
6467
+ if (!existsSync6(root)) return [];
6380
6468
  const files = [];
6381
6469
  const stack = [root];
6382
6470
  while (stack.length > 0) {
@@ -6384,15 +6472,15 @@ function collectJsonlFiles(root, source) {
6384
6472
  if (!current) continue;
6385
6473
  let entries;
6386
6474
  try {
6387
- entries = readdirSync3(current);
6475
+ entries = readdirSync4(current);
6388
6476
  } catch {
6389
6477
  continue;
6390
6478
  }
6391
6479
  for (const entry of entries) {
6392
- const path = join4(current, entry);
6480
+ const path = join5(current, entry);
6393
6481
  let stats;
6394
6482
  try {
6395
- stats = statSync3(path);
6483
+ stats = statSync4(path);
6396
6484
  } catch {
6397
6485
  continue;
6398
6486
  }
@@ -6410,13 +6498,13 @@ function collectJsonlFiles(root, source) {
6410
6498
  function readSessionImport(candidate, root, options) {
6411
6499
  let stats;
6412
6500
  try {
6413
- stats = statSync3(candidate.path);
6501
+ stats = statSync4(candidate.path);
6414
6502
  } catch {
6415
6503
  return null;
6416
6504
  }
6417
6505
  if (stats.size > options.maxBytesPerFile) return null;
6418
6506
  const extractor = candidate.source === "codex" ? extractCodexMessageText : extractClaudeMessageText;
6419
- const lines = readFileSync3(candidate.path, "utf8").split(/\r?\n/);
6507
+ const lines = readFileSync4(candidate.path, "utf8").split(/\r?\n/);
6420
6508
  const relevantLines = [];
6421
6509
  let messageCount = 0;
6422
6510
  for (const line of lines) {
@@ -6428,10 +6516,10 @@ function readSessionImport(candidate, root, options) {
6428
6516
  }
6429
6517
  const deduped = [...new Set(relevantLines)].slice(0, 80);
6430
6518
  if (deduped.length === 0) return null;
6431
- const relativePath = relative2(root, candidate.path);
6519
+ const relativePath = relative3(root, candidate.path);
6432
6520
  return {
6433
6521
  import: {
6434
- sourceId: `${candidate.source}:${basename2(candidate.path, ".jsonl")}`,
6522
+ sourceId: `${candidate.source}:${basename3(candidate.path, ".jsonl")}`,
6435
6523
  sourceLabel: `${candidate.source === "codex" ? "Codex" : "Claude"} session ${relativePath}`,
6436
6524
  metadata: {
6437
6525
  bytes: stats.size,
@@ -6501,11 +6589,11 @@ function loadAiSessionImports(options) {
6501
6589
  }
6502
6590
 
6503
6591
  // src/lib/work-graph-source-adapters.ts
6504
- import { createHash as createHash3 } from "crypto";
6592
+ import { createHash as createHash4 } from "crypto";
6505
6593
  import { execFileSync } from "child_process";
6506
- import { closeSync, existsSync as existsSync6, openSync, readFileSync as readFileSync4, readdirSync as readdirSync4, readSync, statSync as statSync4 } from "fs";
6594
+ import { closeSync, existsSync as existsSync7, openSync, readFileSync as readFileSync5, readdirSync as readdirSync5, readSync, statSync as statSync5 } from "fs";
6507
6595
  import { homedir as homedir2 } from "os";
6508
- import { basename as basename3, join as join5, resolve } from "path";
6596
+ import { basename as basename4, join as join6, resolve } from "path";
6509
6597
  var CLIENT_SOURCES = ["claude_code", "codex", "opencode", "goose", "cursor", "github", "slack"];
6510
6598
  var DEFAULT_LIMIT_PER_SOURCE2 = 8;
6511
6599
  var DEFAULT_SINCE_DAYS2 = 45;
@@ -6524,21 +6612,21 @@ function parseInvestigationSourceList(value) {
6524
6612
  }
6525
6613
  return deduped;
6526
6614
  }
6527
- function hash(value, length = 24) {
6528
- return createHash3("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
6615
+ function hash2(value, length = 24) {
6616
+ return createHash4("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
6529
6617
  }
6530
6618
  function expandPath(path, env) {
6531
6619
  return path.replace(/^~(?=\/|$)/, env.home).replace(/\$CWD/g, env.cwd).replace(/\$HOME/g, env.home);
6532
6620
  }
6533
- function safeStat(path) {
6621
+ function safeStat2(path) {
6534
6622
  try {
6535
- return statSync4(path);
6623
+ return statSync5(path);
6536
6624
  } catch {
6537
6625
  return null;
6538
6626
  }
6539
6627
  }
6540
6628
  function walkFiles(root, predicate, maxFiles = 500) {
6541
- if (!existsSync6(root)) return [];
6629
+ if (!existsSync7(root)) return [];
6542
6630
  const files = [];
6543
6631
  const stack = [root];
6544
6632
  const ignored = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist", "build", ".turbo"]);
@@ -6547,14 +6635,14 @@ function walkFiles(root, predicate, maxFiles = 500) {
6547
6635
  if (!current) continue;
6548
6636
  let entries;
6549
6637
  try {
6550
- entries = readdirSync4(current);
6638
+ entries = readdirSync5(current);
6551
6639
  } catch {
6552
6640
  continue;
6553
6641
  }
6554
6642
  for (const entry of entries) {
6555
6643
  if (ignored.has(entry)) continue;
6556
- const path = join5(current, entry);
6557
- const stats = safeStat(path);
6644
+ const path = join6(current, entry);
6645
+ const stats = safeStat2(path);
6558
6646
  if (!stats) continue;
6559
6647
  if (stats.isDirectory()) {
6560
6648
  stack.push(path);
@@ -6606,10 +6694,10 @@ function nowWindow(timestamp, now) {
6606
6694
  return "older";
6607
6695
  }
6608
6696
  function makeRawEvent(input) {
6609
- const contentHash = hash(input.payload, 64);
6697
+ const contentHash = hash2(input.payload, 64);
6610
6698
  const uri = `${input.client}:${input.sessionId}:${input.uriSuffix}`;
6611
6699
  return {
6612
- event_id: `evt_${hash([uri, contentHash], 24)}`,
6700
+ event_id: `evt_${hash2([uri, contentHash], 24)}`,
6613
6701
  source_ref: {
6614
6702
  source_id: input.client,
6615
6703
  uri,
@@ -6652,7 +6740,7 @@ function parseJsonLine2(line) {
6652
6740
  }
6653
6741
  function readTextWindow(path, stats, maxBytes) {
6654
6742
  if (stats.size <= maxBytes) {
6655
- return { text: readFileSync4(path, "utf8"), truncated: false };
6743
+ return { text: readFileSync5(path, "utf8"), truncated: false };
6656
6744
  }
6657
6745
  const bytesToRead = Math.min(stats.size, maxBytes);
6658
6746
  const buffer = Buffer.alloc(bytesToRead);
@@ -6796,7 +6884,7 @@ function mapClaudeRecord(record, fallbackTimestamp) {
6796
6884
  return out;
6797
6885
  }
6798
6886
  function readJsonlCandidate(candidate, options) {
6799
- const stats = safeStat(candidate.path);
6887
+ const stats = safeStat2(candidate.path);
6800
6888
  if (!stats) {
6801
6889
  return {
6802
6890
  collectionMethods: [],
@@ -6810,7 +6898,7 @@ function readJsonlCandidate(candidate, options) {
6810
6898
  const window = readTextWindow(candidate.path, stats, options.maxBytesPerFile);
6811
6899
  const lines = window.text.split(/\r?\n/);
6812
6900
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
6813
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
6901
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6814
6902
  const events = [];
6815
6903
  for (const [index, line] of lines.entries()) {
6816
6904
  if (!line.trim()) continue;
@@ -6873,7 +6961,7 @@ function mapGenericJsonRecord(record, fallbackTimestamp) {
6873
6961
  }];
6874
6962
  }
6875
6963
  function readJsonCandidate(candidate, options) {
6876
- const stats = safeStat(candidate.path);
6964
+ const stats = safeStat2(candidate.path);
6877
6965
  if (!stats || stats.size > options.maxBytesPerFile) {
6878
6966
  return {
6879
6967
  collectionMethods: [],
@@ -6886,7 +6974,7 @@ function readJsonCandidate(candidate, options) {
6886
6974
  }
6887
6975
  let parsed;
6888
6976
  try {
6889
- parsed = JSON.parse(readFileSync4(candidate.path, "utf8"));
6977
+ parsed = JSON.parse(readFileSync5(candidate.path, "utf8"));
6890
6978
  } catch {
6891
6979
  return {
6892
6980
  collectionMethods: [],
@@ -6899,7 +6987,7 @@ function readJsonCandidate(candidate, options) {
6899
6987
  }
6900
6988
  const records = flattenPotentialMessages(parsed).slice(0, 800);
6901
6989
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
6902
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
6990
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6903
6991
  const events = records.flatMap(
6904
6992
  (record, index) => mapGenericJsonRecord(record, fallbackTimestamp).map(
6905
6993
  (item, partIndex) => makeRawEvent({
@@ -6952,7 +7040,7 @@ function flattenPotentialMessages(value) {
6952
7040
  return out;
6953
7041
  }
6954
7042
  function readMarkdownCandidate(candidate, options) {
6955
- const stats = safeStat(candidate.path);
7043
+ const stats = safeStat2(candidate.path);
6956
7044
  if (!stats || stats.size > options.maxBytesPerFile) {
6957
7045
  return {
6958
7046
  collectionMethods: [],
@@ -6963,8 +7051,8 @@ function readMarkdownCandidate(candidate, options) {
6963
7051
  searchedSessions: 0
6964
7052
  };
6965
7053
  }
6966
- const text2 = readFileSync4(candidate.path, "utf8");
6967
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
7054
+ const text2 = readFileSync5(candidate.path, "utf8");
7055
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6968
7056
  const timestamp = new Date(stats.mtimeMs).toISOString();
6969
7057
  const event = makeRawEvent({
6970
7058
  client: candidate.source,
@@ -6993,7 +7081,7 @@ function readMarkdownCandidate(candidate, options) {
6993
7081
  };
6994
7082
  }
6995
7083
  function readGitReflogCandidate(candidate, options) {
6996
- const stats = safeStat(candidate.path);
7084
+ const stats = safeStat2(candidate.path);
6997
7085
  if (!stats || stats.size > options.maxBytesPerFile) {
6998
7086
  return {
6999
7087
  collectionMethods: [],
@@ -7004,7 +7092,7 @@ function readGitReflogCandidate(candidate, options) {
7004
7092
  searchedSessions: 0
7005
7093
  };
7006
7094
  }
7007
- const lines = readFileSync4(candidate.path, "utf8").split(/\r?\n/).filter(Boolean).slice(-250);
7095
+ const lines = readFileSync5(candidate.path, "utf8").split(/\r?\n/).filter(Boolean).slice(-250);
7008
7096
  const events = [];
7009
7097
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
7010
7098
  for (const [index, line] of lines.entries()) {
@@ -7150,10 +7238,10 @@ function discoverCandidates(source, env, sinceMs) {
7150
7238
  for (const rootPattern of paths) {
7151
7239
  const expanded = expandPath(rootPattern, env);
7152
7240
  const root = expanded.includes("*") ? expanded.slice(0, expanded.indexOf("*")).replace(/\/$/, "") : expanded;
7153
- const exactStats = safeStat(expanded);
7241
+ const exactStats = safeStat2(expanded);
7154
7242
  const found = exactStats?.isFile() ? [expanded] : walkFiles(root, predicate);
7155
7243
  for (const path of found) {
7156
- const stats = safeStat(path);
7244
+ const stats = safeStat2(path);
7157
7245
  if (!stats || stats.mtimeMs < sinceMs) continue;
7158
7246
  candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
7159
7247
  }
@@ -7221,7 +7309,7 @@ function discoverOverrideCandidates(source, root, sinceMs) {
7221
7309
  const files = walkFiles(root, (path) => path.endsWith(".jsonl") || path.endsWith(".json"));
7222
7310
  const candidates = [];
7223
7311
  for (const path of files) {
7224
- const stats = safeStat(path);
7312
+ const stats = safeStat2(path);
7225
7313
  if (!stats || stats.mtimeMs < sinceMs) continue;
7226
7314
  candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
7227
7315
  }
@@ -7296,7 +7384,7 @@ function extractionFromEvents(input) {
7296
7384
  }
7297
7385
  return {
7298
7386
  schema_version: "2.0.0.investigation",
7299
- extraction_id: `${input.client}:investigation:${hash([input.client, input.events.map((event) => event.event_id)], 12)}`,
7387
+ extraction_id: `${input.client}:investigation:${hash2([input.client, input.events.map((event) => event.event_id)], 12)}`,
7300
7388
  source_client: workGraphSourceClient(input.client),
7301
7389
  source_label: clientLabel(input.client),
7302
7390
  collection_methods: [...new Set(input.collectionMethods)].sort(),
@@ -7389,7 +7477,7 @@ function loadWorkGraphInvestigationSourceData(options) {
7389
7477
  }
7390
7478
 
7391
7479
  // src/lib/self-audit.ts
7392
- import { createHash as createHash4 } from "crypto";
7480
+ import { createHash as createHash5 } from "crypto";
7393
7481
  var SELF_AUDIT_SCHEMA_VERSION = "2026-04-27";
7394
7482
  var AUDIT_DIMENSIONS = [
7395
7483
  "queryability",
@@ -7556,7 +7644,7 @@ function buildSelfCritique(scores) {
7556
7644
  });
7557
7645
  }
7558
7646
  function hashPlanPayload(payload) {
7559
- return createHash4("sha256").update(JSON.stringify(payload)).digest("hex");
7647
+ return createHash5("sha256").update(JSON.stringify(payload)).digest("hex");
7560
7648
  }
7561
7649
  function buildSelfAuditPlan(input) {
7562
7650
  if (input.imports.length === 0) {
@@ -7886,10 +7974,10 @@ Rollback: ${plan.recommended_follow_up.rollback}`,
7886
7974
  }
7887
7975
 
7888
7976
  // src/lib/work-graph.ts
7889
- import { createHash as createHash6 } from "crypto";
7977
+ import { createHash as createHash7 } from "crypto";
7890
7978
 
7891
7979
  // src/lib/work-graph-investigation.ts
7892
- import { createHash as createHash5 } from "crypto";
7980
+ import { createHash as createHash6 } from "crypto";
7893
7981
  var WORK_GRAPH_INVESTIGATION_SCHEMA_VERSION = "2.0.0";
7894
7982
  var WORK_GRAPH_INVESTIGATION_CLIENTS = [
7895
7983
  "claude_code",
@@ -8021,8 +8109,8 @@ var CAPABILITY_CEILINGS = {
8021
8109
  decision_lineage: "high"
8022
8110
  }
8023
8111
  };
8024
- function hash2(value, length = 16) {
8025
- return createHash5("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
8112
+ function hash3(value, length = 16) {
8113
+ return createHash6("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
8026
8114
  }
8027
8115
  function clamp(value, min = 0, max = 1) {
8028
8116
  return Math.max(min, Math.min(max, value));
@@ -8075,8 +8163,8 @@ function loopTopicKey(loop) {
8075
8163
  for (const [pattern, topic] of topicRules) {
8076
8164
  if (pattern.test(text2)) return topic;
8077
8165
  }
8078
- if (!intent) return `unclassified:${hash2(loop.loop_id, 10)}`;
8079
- return `topic:${hash2(intent.toLowerCase(), 10)}`;
8166
+ if (!intent) return `unclassified:${hash3(loop.loop_id, 10)}`;
8167
+ return `topic:${hash3(intent.toLowerCase(), 10)}`;
8080
8168
  }
8081
8169
  function bestFamilyCentroid(group) {
8082
8170
  const candidates = group.map((loop) => cleanLoopIntent(loop.origin.intent, "")).filter(Boolean).map((intent) => {
@@ -8140,7 +8228,7 @@ function windowFor(timestamp, now) {
8140
8228
  function rawEventFromFinding(finding, index, generatedAt) {
8141
8229
  const sourceClient = normalizeClient(finding.source_client);
8142
8230
  const timestamp = typeof finding.metadata.occurred_at === "string" ? finding.metadata.occurred_at : generatedAt;
8143
- const eventId = `evt_${hash2([finding.evidence_ref, finding.title, index], 24)}`;
8231
+ const eventId = `evt_${hash3([finding.evidence_ref, finding.title, index], 24)}`;
8144
8232
  const payload = {
8145
8233
  title: finding.title,
8146
8234
  summary: finding.summary,
@@ -8172,7 +8260,7 @@ function rawEventFromFinding(finding, index, generatedAt) {
8172
8260
  payload,
8173
8261
  raw_byte_offset: null,
8174
8262
  raw_row_id: null,
8175
- content_hash: hash2(payload, 64),
8263
+ content_hash: hash3(payload, 64),
8176
8264
  redaction_applied: true
8177
8265
  };
8178
8266
  }
@@ -8184,7 +8272,7 @@ function rawEventFromSourceEvent(event, index, generatedAt) {
8184
8272
  text_summary: event.text.slice(0, 420)
8185
8273
  };
8186
8274
  return {
8187
- event_id: `evt_${hash2([event.evidence_ref, index], 24)}`,
8275
+ event_id: `evt_${hash3([event.evidence_ref, index], 24)}`,
8188
8276
  source_ref: {
8189
8277
  source_id: sourceClient,
8190
8278
  uri: event.evidence_ref,
@@ -8202,7 +8290,7 @@ function rawEventFromSourceEvent(event, index, generatedAt) {
8202
8290
  payload,
8203
8291
  raw_byte_offset: null,
8204
8292
  raw_row_id: null,
8205
- content_hash: hash2(payload, 64),
8293
+ content_hash: hash3(payload, 64),
8206
8294
  redaction_applied: true
8207
8295
  };
8208
8296
  }
@@ -8333,7 +8421,7 @@ function buildWorkLoops(input) {
8333
8421
  trail.confidence || matchedFindings.reduce((total, finding) => total + finding.confidence, 0) / Math.max(1, matchedFindings.length)
8334
8422
  );
8335
8423
  return {
8336
- loop_id: `loop_${hash2([trail.id, index], 18)}`,
8424
+ loop_id: `loop_${hash3([trail.id, index], 18)}`,
8337
8425
  cites: eventIds,
8338
8426
  origin: {
8339
8427
  event_id: eventIds[0] ?? `evt_missing_${index}`,
@@ -8419,7 +8507,7 @@ function buildLoopFamilies(loops, events, impact) {
8419
8507
  (loop) => loop.cites.map((cite) => events.find((event) => event.event_id === cite)?.source_id).filter(Boolean)
8420
8508
  )
8421
8509
  );
8422
- const familyId = `family_${hash2(key, 16)}`;
8510
+ const familyId = `family_${hash3(key, 16)}`;
8423
8511
  const timestamps = group.map((loop) => Date.parse(loop.origin.timestamp)).filter(Number.isFinite);
8424
8512
  const spanDays = timestamps.length > 1 ? Math.max(1, Math.ceil((Math.max(...timestamps) - Math.min(...timestamps)) / 864e5)) : 0;
8425
8513
  const publicLoopCount = group.filter((loop) => loop.public_surface).length;
@@ -8772,7 +8860,7 @@ function counterfactualForLoop(loop, family) {
8772
8860
  orgx_outcome: repair.expected_outcome,
8773
8861
  resulting_entity: {
8774
8862
  kind: entityKind,
8775
- inferred_id: `orgx_${entityKind}_${hash2(loop.loop_id, 12)}`,
8863
+ inferred_id: `orgx_${entityKind}_${hash3(loop.loop_id, 12)}`,
8776
8864
  would_link_to: loop.cites
8777
8865
  },
8778
8866
  realism_score: Number(realism.toFixed(2)),
@@ -8983,7 +9071,7 @@ function buildMirror(input) {
8983
9071
  };
8984
9072
  }
8985
9073
  function buildWorkGraphInvestigation(input) {
8986
- const auditId = `wgi_${hash2([input.fingerprint, input.generatedAt], 24)}`;
9074
+ const auditId = `wgi_${hash3([input.fingerprint, input.generatedAt], 24)}`;
8987
9075
  const rawEvents = buildRawEvents(input);
8988
9076
  const corpus = buildCorpusManifest({
8989
9077
  auditId,
@@ -9124,7 +9212,7 @@ function clampScore2(value) {
9124
9212
  return Math.max(0, Math.min(100, Math.round(value)));
9125
9213
  }
9126
9214
  function hashJson(value) {
9127
- return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
9215
+ return createHash7("sha256").update(JSON.stringify(value)).digest("hex");
9128
9216
  }
9129
9217
  function normalizeFingerprintText(value) {
9130
9218
  return value.toLowerCase().replace(/https?:\/\/\S+/g, "url").replace(/[0-9a-f]{12,}/g, "hash").replace(/\b[0-9a-f]{8}-[0-9a-f-]{27,}\b/g, "uuid").replace(/\s+/g, " ").trim().slice(0, 600);
@@ -12215,14 +12303,14 @@ function renderWorkGraphMarkdown(report, options = {}) {
12215
12303
  }
12216
12304
 
12217
12305
  // src/lib/work-graph-publish.ts
12218
- import { createHash as createHash7, randomUUID as randomUUID2 } from "crypto";
12306
+ import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
12219
12307
  import { gzipSync } from "zlib";
12220
12308
  var WORK_GRAPH_REPORT_GZIP_THRESHOLD_BYTES = 4e6;
12221
12309
  var WORK_GRAPH_REPORT_CHUNK_UPLOAD_THRESHOLD_BYTES = 5e5;
12222
12310
  var WORK_GRAPH_REPORT_CHUNK_CHARS = 3e4;
12223
12311
  var WORK_GRAPH_REPORT_CHUNK_UPLOAD_TIMEOUT_MS = 3e5;
12224
12312
  function hashText(value) {
12225
- return createHash7("sha256").update(value).digest("hex");
12313
+ return createHash8("sha256").update(value).digest("hex");
12226
12314
  }
12227
12315
  function buildWorkGraphReportPostPayload(report, options = {}) {
12228
12316
  return {
@@ -12429,8 +12517,8 @@ async function publishWorkGraphEvents(fingerprint, patch, options = {}) {
12429
12517
  }
12430
12518
 
12431
12519
  // src/lib/work-graph-hook-events.ts
12432
- import { createHash as createHash8 } from "crypto";
12433
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
12520
+ import { createHash as createHash9 } from "crypto";
12521
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
12434
12522
  var SOURCE_CLIENTS = [
12435
12523
  "codex",
12436
12524
  "claude",
@@ -12464,7 +12552,7 @@ function asStringArray(value) {
12464
12552
  return value.filter((item) => typeof item === "string" && item.trim().length > 0);
12465
12553
  }
12466
12554
  function stableHash(value) {
12467
- return createHash8("sha256").update(value).digest("hex").slice(0, 20);
12555
+ return createHash9("sha256").update(value).digest("hex").slice(0, 20);
12468
12556
  }
12469
12557
  function normalizeSourceClient2(value) {
12470
12558
  const raw = asString2(value)?.toLowerCase();
@@ -12531,8 +12619,8 @@ function readHookRecord(line) {
12531
12619
  }
12532
12620
  }
12533
12621
  function readRuntimeHookOutbox(path, limit = 200) {
12534
- if (!existsSync7(path)) return { path, records: [], skipped: 0 };
12535
- const lines = readFileSync5(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
12622
+ if (!existsSync8(path)) return { path, records: [], skipped: 0 };
12623
+ const lines = readFileSync6(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
12536
12624
  const selected = lines.slice(Math.max(0, lines.length - Math.max(1, limit)));
12537
12625
  const records = [];
12538
12626
  let skipped = Math.max(0, lines.length - selected.length);
@@ -12668,20 +12756,20 @@ function buildWorkGraphHookReplayPatch(readResult) {
12668
12756
  }
12669
12757
 
12670
12758
  // src/lib/runtime-hooks.ts
12671
- import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
12759
+ import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
12672
12760
  import { homedir as homedir3 } from "os";
12673
- import { dirname as dirname4, join as join6 } from "path";
12761
+ import { dirname as dirname4, join as join7 } from "path";
12674
12762
  var HOOK_MARKER = "orgx-session-hook.mjs";
12675
12763
  var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
12676
12764
  var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
12677
12765
  function defaultPaths(options = {}) {
12678
- const hookDir = join6(ORGX_WIZARD_CONFIG_HOME, "hooks");
12766
+ const hookDir = join7(ORGX_WIZARD_CONFIG_HOME, "hooks");
12679
12767
  return {
12680
- claudeSettingsPath: options.claudeSettingsPath ?? join6(CLAUDE_DIR, "settings.json"),
12681
- codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join6(CODEX_DIR, "config.toml"),
12682
- codexHooksPath: options.codexHooksPath ?? join6(CODEX_DIR, "hooks.json"),
12683
- hookScriptPath: options.hookScriptPath ?? join6(hookDir, HOOK_MARKER),
12684
- outboxPath: options.outboxPath ?? join6(hookDir, "events.jsonl")
12768
+ claudeSettingsPath: options.claudeSettingsPath ?? join7(CLAUDE_DIR, "settings.json"),
12769
+ codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join7(CODEX_DIR, "config.toml"),
12770
+ codexHooksPath: options.codexHooksPath ?? join7(CODEX_DIR, "hooks.json"),
12771
+ hookScriptPath: options.hookScriptPath ?? join7(hookDir, HOOK_MARKER),
12772
+ outboxPath: options.outboxPath ?? join7(hookDir, "events.jsonl")
12685
12773
  };
12686
12774
  }
12687
12775
  function countJsonlLines(path) {
@@ -12694,7 +12782,7 @@ function backupPath(path, now) {
12694
12782
  return `${path}.bak.${timestamp}`;
12695
12783
  }
12696
12784
  function backupExisting(path, now) {
12697
- if (!existsSync8(path)) return null;
12785
+ if (!existsSync9(path)) return null;
12698
12786
  const backup = backupPath(path, now);
12699
12787
  copyFileSync(path, backup);
12700
12788
  return backup;
@@ -12892,7 +12980,7 @@ function inspectRuntimeHooks(options = {}) {
12892
12980
  installed: {
12893
12981
  claudeCode: hasOrgxHook(claudeSettingsRaw),
12894
12982
  codex: hasOrgxHook(codexHooksRaw),
12895
- hookScript: existsSync8(paths.hookScriptPath)
12983
+ hookScript: existsSync9(paths.hookScriptPath)
12896
12984
  },
12897
12985
  codex: {
12898
12986
  configExists: Boolean(codexConfigRaw),
@@ -13154,10 +13242,10 @@ async function runHookReplayCommand(options) {
13154
13242
  }
13155
13243
  function readAuditInput(options, interactive) {
13156
13244
  if (options.input?.trim()) {
13157
- return readFileSync7(resolve2(options.input.trim()), "utf8");
13245
+ return readFileSync8(resolve2(options.input.trim()), "utf8");
13158
13246
  }
13159
13247
  if (!process.stdin.isTTY) {
13160
- return readFileSync7(0, "utf8");
13248
+ return readFileSync8(0, "utf8");
13161
13249
  }
13162
13250
  if (!interactive) {
13163
13251
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -13190,7 +13278,7 @@ function collectPathOption(value, previous = []) {
13190
13278
  }
13191
13279
  function parseClientExtractionFile(path) {
13192
13280
  const resolvedPath = resolve2(path);
13193
- const parsed = JSON.parse(readFileSync7(resolvedPath, "utf8"));
13281
+ const parsed = JSON.parse(readFileSync8(resolvedPath, "utf8"));
13194
13282
  if (!isRecord(parsed)) {
13195
13283
  throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
13196
13284
  }
@@ -13675,6 +13763,40 @@ function printSkillExtensions(extensions) {
13675
13763
  );
13676
13764
  }
13677
13765
  }
13766
+ function printLocalSkillCandidates(candidates) {
13767
+ if (candidates.length === 0) {
13768
+ console.log(` ${ICON.skip} ${pc3.dim("no local skill files found")}`);
13769
+ return;
13770
+ }
13771
+ candidates.forEach((candidate, index) => {
13772
+ const domains = candidate.agentDomains.join(", ");
13773
+ console.log(
13774
+ ` ${ICON.ok} ${pc3.bold(String(index + 1).padStart(2, " "))} ${pc3.bold(candidate.title)} ${pc3.dim(candidate.id)}`
13775
+ );
13776
+ console.log(` ${pc3.dim(`${candidate.source} \xB7 ${domains} \xB7 ${candidate.reason}`)}`);
13777
+ console.log(` ${pc3.dim(candidate.path)}`);
13778
+ if (candidate.snippet) {
13779
+ console.log(` ${pc3.dim(candidate.snippet)}`);
13780
+ }
13781
+ });
13782
+ }
13783
+ function writeLocalSkillSelection(input) {
13784
+ const scope = input.scope ?? "user";
13785
+ const nextContent = buildLocalSkillExtensionContent(input.candidates, input.context);
13786
+ const existing = listSkillExtensions().find(
13787
+ (extension) => extension.skillId === "orgx" && extension.scope === scope
13788
+ );
13789
+ const content = existing?.content.trim() ? `${existing.content.trimEnd()}
13790
+
13791
+ ${nextContent}` : nextContent;
13792
+ return addSkillExtension({
13793
+ content,
13794
+ overwrite: true,
13795
+ scope,
13796
+ skillId: "orgx",
13797
+ title: "Local skill preferences"
13798
+ });
13799
+ }
13678
13800
  function printSkillExtensionWrite(result) {
13679
13801
  const action = result.created ? "created" : result.changed ? "updated" : "unchanged";
13680
13802
  const color = result.created || result.changed ? pc3.green : pc3.dim;
@@ -13740,6 +13862,8 @@ function printSetupScopeNote() {
13740
13862
  }
13741
13863
  function printFirstValueHandoff(input) {
13742
13864
  const cmd = getCmd();
13865
+ const fallbackPrompt = `Use OrgX to continue "${input.initiative.initiative.title}" in ${input.workspace.name}. Show the next action, then start with the onboarding task.`;
13866
+ const handoffPrompt = buildProfileHandoffPrompt(input.profile, fallbackPrompt, input.context);
13743
13867
  console.log("");
13744
13868
  console.log(pc3.bold("first OrgX handoff"));
13745
13869
  console.log(
@@ -13748,9 +13872,13 @@ function printFirstValueHandoff(input) {
13748
13872
  console.log(` live: ${input.initiative.liveUrl}`);
13749
13873
  console.log(
13750
13874
  ` ${pc3.dim("ask your AI tool:")} ${pc3.cyan(
13751
- `Use OrgX to continue "${input.initiative.initiative.title}" in ${input.workspace.name}. Show the next action, then start with the onboarding task.`
13875
+ handoffPrompt
13752
13876
  )}`
13753
13877
  );
13878
+ if (input.profile) {
13879
+ console.log(` ${pc3.dim("local proof:")} ${pc3.cyan(input.profile.localProofCommand)}`);
13880
+ console.log(` ${pc3.dim("skills:")} ${pc3.cyan(input.profile.skillDiscoveryCommand)}`);
13881
+ }
13754
13882
  console.log(` ${pc3.dim("later:")} ${pc3.cyan(`${cmd} doctor`)} ${pc3.dim("checks tool wiring")}`);
13755
13883
  }
13756
13884
  function firstValueRecordToResult(record) {
@@ -13775,20 +13903,6 @@ function printFounderPresetResult(result) {
13775
13903
  console.log("");
13776
13904
  printWorkspaceSetupResult(result.workspaceSetup);
13777
13905
  }
13778
- if (result.demoInitiative) {
13779
- console.log("");
13780
- console.log(pc3.bold("demo initiative"));
13781
- console.log(
13782
- ` ${result.demoInitiative.created ? pc3.green("created") : pc3.yellow("unchanged")} ${result.demoInitiative.initiative.title}`
13783
- );
13784
- console.log(` live: ${result.demoInitiative.liveUrl}`);
13785
- console.log(
13786
- ` decision: ${result.demoInitiative.decision.title} ${pc3.dim(`(${result.demoInitiative.decision.status ?? "pending"})`)}`
13787
- );
13788
- console.log(
13789
- ` artifact: ${result.demoInitiative.artifact.name}${result.demoInitiative.artifact.url ? ` ${pc3.dim(result.demoInitiative.artifact.url)}` : ""}`
13790
- );
13791
- }
13792
13906
  }
13793
13907
  function normalizePromptResult(value) {
13794
13908
  return value;
@@ -14213,10 +14327,9 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14213
14327
  }
14214
14328
  const existingState = readWizardState();
14215
14329
  const providedInitiativeId = input.initiativeId?.trim();
14216
- const storedDemoInitiative = existingState?.demoInitiative?.workspaceId === input.workspace.id ? existingState.demoInitiative : void 0;
14217
14330
  const storedFirstValueInitiative = existingState?.firstValueInitiative?.workspaceId === input.workspace.id ? existingState.firstValueInitiative : void 0;
14218
- let effectiveInitiativeId = providedInitiativeId || storedDemoInitiative?.id || storedFirstValueInitiative?.id;
14219
- let firstValueInitiative = providedInitiativeId || storedDemoInitiative ? null : firstValueRecordToResult(storedFirstValueInitiative);
14331
+ let effectiveInitiativeId = providedInitiativeId || storedFirstValueInitiative?.id;
14332
+ let firstValueInitiative = providedInitiativeId ? null : firstValueRecordToResult(storedFirstValueInitiative);
14220
14333
  const firstInitiativeSkipped = hasSetupPromptSkip(
14221
14334
  input.workspace.id,
14222
14335
  "first_initiative"
@@ -14240,7 +14353,7 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14240
14353
  }
14241
14354
  if (firstInitiativeChoice === "yes") {
14242
14355
  const title = await textPrompt({
14243
- initialValue: FIRST_VALUE_INITIATIVE_TITLE,
14356
+ initialValue: input.profile?.firstInitiativeTitle ?? FIRST_VALUE_INITIATIVE_TITLE,
14244
14357
  message: "What should OrgX help you move forward first?",
14245
14358
  validate: (value) => {
14246
14359
  if (!value || value.trim().length === 0) {
@@ -14257,6 +14370,7 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14257
14370
  spinner.start();
14258
14371
  try {
14259
14372
  firstValueInitiative = await ensureFirstValueInitiative(input.workspace, {
14373
+ ...input.context || input.profile ? { summary: input.profile ? buildProfileSummary(input.profile, input.context) : input.context } : {},
14260
14374
  title: String(title)
14261
14375
  });
14262
14376
  spinner.succeed(
@@ -14347,7 +14461,9 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14347
14461
  }
14348
14462
  if (firstValueInitiative) {
14349
14463
  printFirstValueHandoff({
14464
+ ...input.context ? { context: input.context } : {},
14350
14465
  initiative: firstValueInitiative,
14466
+ ...input.profile !== void 0 ? { profile: input.profile } : {},
14351
14467
  workspace: input.workspace
14352
14468
  });
14353
14469
  }
@@ -14420,6 +14536,64 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14420
14536
  });
14421
14537
  }
14422
14538
  }
14539
+ const localSkillDiscoverySkipped = hasSetupPromptSkip(
14540
+ input.workspace.id,
14541
+ "local_skill_discovery"
14542
+ );
14543
+ if (!localSkillDiscoverySkipped) {
14544
+ const context = input.context?.trim();
14545
+ const candidates = discoverLocalSkills({
14546
+ ...context ? { context } : {},
14547
+ limit: 8
14548
+ });
14549
+ if (candidates.length > 0) {
14550
+ const selectedSkillIds = await multiselectPrompt({
14551
+ message: "Bring existing local skills into your OrgX agents?",
14552
+ options: [
14553
+ ...candidates.map((candidate, index) => ({
14554
+ value: candidate.id,
14555
+ label: `${index + 1}. ${candidate.title}`,
14556
+ hint: `${candidate.source} \xB7 ${candidate.agentDomains.join(", ")} \xB7 ${candidate.reason}`
14557
+ })),
14558
+ {
14559
+ value: "__skip__",
14560
+ label: "Skip local skill import",
14561
+ hint: "You can run `orgx-wizard skills discover-local` later."
14562
+ }
14563
+ ],
14564
+ required: false
14565
+ });
14566
+ if (clack.isCancel(selectedSkillIds)) {
14567
+ clack.cancel("Setup cancelled.");
14568
+ return "cancelled";
14569
+ }
14570
+ const selected = candidates.filter((candidate) => selectedSkillIds.includes(candidate.id));
14571
+ if (selectedSkillIds.includes("__skip__") || selected.length === 0) {
14572
+ recordSetupPromptSkip({
14573
+ promptKey: "local_skill_discovery",
14574
+ workspaceId: input.workspace.id,
14575
+ workspaceName: input.workspace.name
14576
+ });
14577
+ console.log(` ${ICON.skip} ${pc3.dim("local skills skipped")}`);
14578
+ } else {
14579
+ const result = writeLocalSkillSelection({
14580
+ candidates: selected,
14581
+ ...context ? { context } : {}
14582
+ });
14583
+ printSkillExtensionWrite(result);
14584
+ console.log(
14585
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply selected local preferences to configured tools.`)}`
14586
+ );
14587
+ await safeTrackWizardTelemetry("local_skills_configured", {
14588
+ candidate_count: candidates.length,
14589
+ command: input.telemetry?.command ?? "setup",
14590
+ selected_count: selected.length,
14591
+ ...input.telemetry?.preset ? { preset: input.telemetry.preset } : {},
14592
+ profile: input.profile?.id ?? "none"
14593
+ });
14594
+ }
14595
+ }
14596
+ }
14423
14597
  return "configured";
14424
14598
  }
14425
14599
  async function promptOptionalCompanionPluginTargets(input) {
@@ -14509,7 +14683,7 @@ function printAuthStatus(status) {
14509
14683
  }
14510
14684
  }
14511
14685
  function printDoctorReport(report, assessment) {
14512
- const verification = summarizeSetupVerification(assessment);
14686
+ const verification = summarizeSetupVerification(assessment, report);
14513
14687
  console.log(pc3.dim(" surfaces"));
14514
14688
  printSurfaceTable(report.surfaces);
14515
14689
  console.log("");
@@ -14557,6 +14731,9 @@ function printDoctorReport(report, assessment) {
14557
14731
  } else {
14558
14732
  console.log(` ${pc3.dim("\u2192")} ${pc3.dim(`OrgX is active across ${configuredCount} editor${configuredCount !== 1 ? "s" : ""}`)}`);
14559
14733
  }
14734
+ } else if (verification.transientTimeouts) {
14735
+ console.log(` ${ICON.warn} ${pc3.yellow(getSetupVerificationHeadline(verification))}`);
14736
+ console.log(` ${pc3.dim("\u2192")} ${pc3.cyan(`${getCmd()} doctor`)} ${pc3.dim("re-runs the health checks")}`);
14560
14737
  } else {
14561
14738
  const headlineText = getSetupVerificationHeadline(verification);
14562
14739
  const headline = verification.status === "error" ? `${ICON.err} ${pc3.red(headlineText)}` : `${ICON.warn} ${pc3.yellow(headlineText)}`;
@@ -14573,20 +14750,26 @@ function printDoctorReport(report, assessment) {
14573
14750
  async function main() {
14574
14751
  const program = new Command();
14575
14752
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
14576
- const pkgVersion = true ? "0.1.45" : void 0;
14753
+ const pkgVersion = true ? "0.1.47" : void 0;
14577
14754
  program.version(pkgVersion ?? "unknown", "-V, --version");
14578
14755
  program.hook("preAction", (_thisCommand, actionCommand) => {
14579
14756
  if (Boolean(actionCommand.optsWithGlobals().json)) return;
14580
14757
  console.log(renderBanner(pkgVersion));
14581
14758
  });
14582
- 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("--workspace", "choose or change the default workspace during setup").option("--daily-brief", "configure Daily Brief even if setup already handled it").option("--skip-daily-brief", "skip Daily Brief prompts and remember the skip").action(async (options) => {
14759
+ 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("--profile <name>", `tailor setup for a workflow profile (${supportedSetupProfileIds().join(", ")})`).option("--context <text>", "extra workflow context to include in the first initiative and handoff").option("--workspace", "choose or change the default workspace during setup").option("--daily-brief", "configure Daily Brief even if setup already handled it").option("--skip-daily-brief", "skip Daily Brief prompts and remember the skip").action(async (options) => {
14583
14760
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
14584
14761
  if (options.dailyBrief && options.skipDailyBrief) {
14585
14762
  throw new Error("Use either --daily-brief or --skip-daily-brief, not both.");
14586
14763
  }
14764
+ const setupProfile = resolveSetupProfile(options.profile);
14765
+ if (options.profile && !setupProfile) {
14766
+ throw new Error(`Unknown setup profile '${options.profile}'. Supported profiles: ${supportedSetupProfileIds().join(", ")}.`);
14767
+ }
14768
+ const setupContext = options.context?.trim() || void 0;
14587
14769
  await safeTrackWizardTelemetry("wizard_started", {
14588
14770
  command: "setup",
14589
14771
  interactive,
14772
+ profile: setupProfile?.id ?? "none",
14590
14773
  preset: options.preset ?? "standard"
14591
14774
  });
14592
14775
  printSetupScopeNote();
@@ -14646,20 +14829,12 @@ async function main() {
14646
14829
  })
14647
14830
  );
14648
14831
  }
14649
- if (presetResult.demoInitiative) {
14650
- await safeTrackWizardTelemetry(
14651
- "founder_demo_ready",
14652
- buildFounderDemoTelemetryProperties(presetResult.demoInitiative, {
14653
- command: "setup",
14654
- preset: "founder"
14655
- })
14656
- );
14657
- }
14658
14832
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
14833
+ ...setupContext ? { context: setupContext } : {},
14659
14834
  interactive,
14835
+ profile: setupProfile,
14660
14836
  telemetry: { command: "setup", preset: "founder" },
14661
- workspace: presetResult.workspace,
14662
- ...presetResult.demoInitiative ? { initiativeId: presetResult.demoInitiative.initiative.id } : {}
14837
+ workspace: presetResult.workspace
14663
14838
  });
14664
14839
  if (addOnResult === "cancelled") {
14665
14840
  return;
@@ -14671,7 +14846,7 @@ async function main() {
14671
14846
  console.log("");
14672
14847
  const doctor2 = await runDoctor();
14673
14848
  const assessment2 = assessDoctorReport(doctor2);
14674
- const verification2 = summarizeSetupVerification(assessment2);
14849
+ const verification2 = summarizeSetupVerification(assessment2, doctor2);
14675
14850
  await safeTrackWizardTelemetry(
14676
14851
  "setup_verified",
14677
14852
  buildDoctorTelemetryProperties(doctor2, assessment2, verification2, {
@@ -14698,6 +14873,7 @@ async function main() {
14698
14873
  });
14699
14874
  const wasAlreadyPaired = await resolveOrgxAuth() !== null;
14700
14875
  let resolvedAuth = await resolveOrgxAuth();
14876
+ let cachedWorkspaceCheck;
14701
14877
  if (!resolvedAuth) {
14702
14878
  console.log("");
14703
14879
  if (interactive) {
@@ -14781,6 +14957,20 @@ async function main() {
14781
14957
  })
14782
14958
  );
14783
14959
  const resolvedWorkspace = workspaceSetup.workspace ?? await getCurrentWorkspace().catch(() => null);
14960
+ if (resolvedWorkspace && resolvedAuth) {
14961
+ cachedWorkspaceCheck = {
14962
+ configured: true,
14963
+ ok: true,
14964
+ skipped: false,
14965
+ source: resolvedAuth.source,
14966
+ baseUrl: resolvedAuth.baseUrl,
14967
+ workspace: resolvedWorkspace,
14968
+ details: [
14969
+ `workspace id: ${resolvedWorkspace.id}`,
14970
+ ...resolvedWorkspace.isDefault ? ["workspace is marked as default"] : []
14971
+ ]
14972
+ };
14973
+ }
14784
14974
  if (resolvedWorkspace) {
14785
14975
  persistContinuityDefaults({ workspace: resolvedWorkspace });
14786
14976
  }
@@ -14817,7 +15007,9 @@ async function main() {
14817
15007
  return;
14818
15008
  }
14819
15009
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
15010
+ ...setupContext ? { context: setupContext } : {},
14820
15011
  interactive,
15012
+ profile: setupProfile,
14821
15013
  telemetry: { command: "setup", preset: "standard" },
14822
15014
  workspace: resolvedWorkspace
14823
15015
  });
@@ -14833,9 +15025,11 @@ async function main() {
14833
15025
  return;
14834
15026
  }
14835
15027
  }
14836
- const doctor = await runDoctor();
15028
+ const doctor = await runDoctor(
15029
+ cachedWorkspaceCheck ? { cachedWorkspace: cachedWorkspaceCheck } : {}
15030
+ );
14837
15031
  const assessment = assessDoctorReport(doctor);
14838
- const verification = summarizeSetupVerification(assessment);
15032
+ const verification = summarizeSetupVerification(assessment, doctor);
14839
15033
  await safeTrackWizardTelemetry(
14840
15034
  "setup_verified",
14841
15035
  buildDoctorTelemetryProperties(doctor, assessment, verification, {
@@ -15352,7 +15546,7 @@ async function main() {
15352
15546
  const report = await runDoctor();
15353
15547
  spinner.stop();
15354
15548
  const assessment = assessDoctorReport(report);
15355
- const verification = summarizeSetupVerification(assessment);
15549
+ const verification = summarizeSetupVerification(assessment, report);
15356
15550
  await safeTrackWizardTelemetry(
15357
15551
  "doctor_ran",
15358
15552
  buildDoctorTelemetryProperties(report, assessment, verification, {
@@ -15421,6 +15615,48 @@ async function main() {
15421
15615
  }
15422
15616
  }
15423
15617
  });
15618
+ skills.command("discover-local").description("Inventory local skill files and optionally fold selected ones into the OrgX base skill extension.").option("--from <sources>", "local sources to scan: opencode, claude, codex, agents, workspace, or all", "all").option("--context <text>", "workflow context used to rank and annotate discovered skills").option("--limit <count>", "max candidates to show", "12").option("--apply <selection>", "comma-separated candidate numbers or ids to opt into the OrgX base skill extension").option("--scope <scope>", "extension scope: user, workspace, or project", "user").option("--json", "emit a JSON summary").action(async (options) => {
15619
+ const sources = parseLocalSkillSources(options.from);
15620
+ const limit = parsePositiveInteger(options.limit, 12, "--limit");
15621
+ const candidates = discoverLocalSkills({
15622
+ ...options.context?.trim() ? { context: options.context.trim() } : {},
15623
+ limit,
15624
+ sources
15625
+ });
15626
+ let extensionWrite = null;
15627
+ if (options.apply?.trim()) {
15628
+ const selected = selectLocalSkillCandidates(candidates, options.apply);
15629
+ extensionWrite = writeLocalSkillSelection({
15630
+ candidates: selected,
15631
+ ...options.context?.trim() ? { context: options.context.trim() } : {},
15632
+ scope: options.scope ?? "user"
15633
+ });
15634
+ }
15635
+ if (options.json) {
15636
+ console.log(JSON.stringify({
15637
+ applied: extensionWrite ? {
15638
+ changed: extensionWrite.changed,
15639
+ created: extensionWrite.created,
15640
+ path: extensionWrite.path
15641
+ } : null,
15642
+ candidates
15643
+ }, null, 2));
15644
+ return;
15645
+ }
15646
+ printLocalSkillCandidates(candidates);
15647
+ if (extensionWrite) {
15648
+ console.log("");
15649
+ printSkillExtensionWrite(extensionWrite);
15650
+ console.log(
15651
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply selected local preferences to configured tools.`)}`
15652
+ );
15653
+ } else if (candidates.length > 0) {
15654
+ console.log("");
15655
+ console.log(
15656
+ ` ${ICON.skip} ${pc3.dim(`Opt in with ${getCmd()} skills discover-local --apply 1,2, then run ${getCmd()} skills sync.`)}`
15657
+ );
15658
+ }
15659
+ });
15424
15660
  const skillExtensions = skills.command("extensions").description("Create, edit, and sync user extensions appended after OrgX core skills.");
15425
15661
  skillExtensions.command("list").description("List local OrgX skill extensions.").action(() => {
15426
15662
  printSkillExtensions(listSkillExtensions());