@useorgx/wizard 0.1.45 → 0.1.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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";
@@ -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)) {
@@ -3215,8 +3215,8 @@ function encodeRepoPath2(value) {
3215
3215
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
3216
3216
  }
3217
3217
  function isLikelyRepoFilePath(path) {
3218
- const basename4 = path.split("/").pop() ?? path;
3219
- return basename4.includes(".") && !/^\.[^./]+$/.test(basename4);
3218
+ const basename5 = path.split("/").pop() ?? path;
3219
+ return basename5.includes(".") && !/^\.[^./]+$/.test(basename5);
3220
3220
  }
3221
3221
  function buildContentsUrl2(spec, path) {
3222
3222
  const encodedPath = encodeRepoPath2(path);
@@ -5392,6 +5392,207 @@ async function runFounderPreset(prompts, options) {
5392
5392
  };
5393
5393
  }
5394
5394
 
5395
+ // src/lib/local-skill-discovery.ts
5396
+ import { createHash as createHash3 } from "crypto";
5397
+ import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
5398
+ import { basename as basename2, join as join4, relative as relative2 } from "path";
5399
+ var DEFAULT_MAX_BYTES = 48e3;
5400
+ var DEFAULT_LIMIT = 12;
5401
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", ".next", ".turbo", "build", "dist", "node_modules"]);
5402
+ var LOCAL_SKILL_SOURCES = ["opencode", "claude", "codex", "agents", "workspace"];
5403
+ var DOMAIN_KEYWORDS = [
5404
+ { domain: "engineering", pattern: /\b(eval|benchmark|test|ci|code|github|repo|runtime|api|llm|model)\b/i },
5405
+ { domain: "product", pattern: /\b(customer|workflow|roadmap|prd|user|adoption|value|requirements?)\b/i },
5406
+ { domain: "operations", pattern: /\b(runbook|incident|sla|dashboard|superset|metric|monitor|ops)\b/i },
5407
+ { domain: "design", pattern: /\b(ui|ux|design|accessibility|component|visual)\b/i },
5408
+ { domain: "marketing", pattern: /\b(launch|campaign|positioning|gtm|content|story)\b/i },
5409
+ { domain: "sales", pattern: /\b(deal|pipeline|prospect|meddic|outreach|buyer)\b/i }
5410
+ ];
5411
+ function hash(value, length = 10) {
5412
+ return createHash3("sha256").update(value).digest("hex").slice(0, length);
5413
+ }
5414
+ function safeStat(path) {
5415
+ try {
5416
+ return statSync3(path);
5417
+ } catch {
5418
+ return null;
5419
+ }
5420
+ }
5421
+ function defaultRoots(input) {
5422
+ return {
5423
+ agents: [join4(input.home, ".agents", "skills")],
5424
+ claude: [join4(input.home, ".claude", "skills"), join4(input.cwd, ".claude", "skills")],
5425
+ codex: [join4(input.home, ".codex", "skills"), join4(input.cwd, ".codex", "skills")],
5426
+ opencode: [
5427
+ join4(input.home, ".opencode", "skills"),
5428
+ join4(input.home, ".config", "opencode", "skills"),
5429
+ join4(input.home, "Library", "Application Support", "opencode", "skills"),
5430
+ join4(input.cwd, ".opencode", "skills")
5431
+ ],
5432
+ workspace: [
5433
+ join4(input.cwd, "skills"),
5434
+ join4(input.cwd, ".agents", "skills"),
5435
+ join4(input.cwd, ".orgx", "skills")
5436
+ ]
5437
+ };
5438
+ }
5439
+ function walkSkillFiles(root, maxFiles = 200) {
5440
+ const rootStats = safeStat(root);
5441
+ if (!rootStats) return [];
5442
+ if (rootStats.isFile()) return [root];
5443
+ if (!rootStats.isDirectory()) return [];
5444
+ const files = [];
5445
+ const stack = [root];
5446
+ while (stack.length > 0 && files.length < maxFiles) {
5447
+ const current = stack.pop();
5448
+ if (!current) continue;
5449
+ let entries;
5450
+ try {
5451
+ entries = readdirSync3(current, { withFileTypes: true });
5452
+ } catch {
5453
+ continue;
5454
+ }
5455
+ for (const entry of entries) {
5456
+ if (IGNORED_DIRS.has(entry.name)) continue;
5457
+ const path = join4(current, entry.name);
5458
+ if (entry.isDirectory()) {
5459
+ stack.push(path);
5460
+ } else if (entry.isFile() && /\.(md|mdc|txt)$/i.test(entry.name)) {
5461
+ files.push(path);
5462
+ }
5463
+ }
5464
+ }
5465
+ return files;
5466
+ }
5467
+ function readWindow(path, maxBytes) {
5468
+ const stats = safeStat(path);
5469
+ if (!stats?.isFile() || stats.size === 0) return null;
5470
+ try {
5471
+ const text2 = readFileSync3(path, "utf8");
5472
+ return text2.slice(0, maxBytes);
5473
+ } catch {
5474
+ return null;
5475
+ }
5476
+ }
5477
+ function titleFrom(path, text2) {
5478
+ const heading = text2.match(/^#\s+(.+)$/m)?.[1]?.trim();
5479
+ if (heading) return heading.slice(0, 120);
5480
+ return basename2(path).replace(/\.(md|mdc|txt)$/i, "").replace(/[-_]+/g, " ");
5481
+ }
5482
+ function snippetFrom(text2) {
5483
+ const lines = text2.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("---") && !/^#+\s/.test(line));
5484
+ return lines.slice(0, 4).join(" ").replace(/\s+/g, " ").slice(0, 420);
5485
+ }
5486
+ function tokenize(value) {
5487
+ return value.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length >= 3);
5488
+ }
5489
+ function scoreCandidate(text2, context, source) {
5490
+ const haystack = text2.toLowerCase();
5491
+ const contextTokens = [...new Set(tokenize(context))];
5492
+ const contextHits = contextTokens.filter((token) => haystack.includes(token)).length;
5493
+ const explicitSkillSignal = /\b(skill|agent|workflow|instruction|rule|prompt|playbook)\b/i.test(text2) ? 2 : 0;
5494
+ const sourceBoost = source === "opencode" ? 2 : source === "workspace" ? 1 : 0;
5495
+ const score = contextHits * 3 + explicitSkillSignal + sourceBoost;
5496
+ const reasonParts = [
5497
+ contextHits > 0 ? `${contextHits} context match${contextHits === 1 ? "" : "es"}` : "local skill file",
5498
+ sourceBoost > 0 ? `${source} source` : ""
5499
+ ].filter(Boolean);
5500
+ return { reason: reasonParts.join("; "), score };
5501
+ }
5502
+ function inferDomains(text2, context) {
5503
+ const joined = `${text2}
5504
+ ${context}`;
5505
+ const domains = DOMAIN_KEYWORDS.filter((entry) => entry.pattern.test(joined)).map((entry) => entry.domain);
5506
+ return domains.length > 0 ? [...new Set(domains)] : ["orchestrator"];
5507
+ }
5508
+ function parseLocalSkillSources(value) {
5509
+ if (!value?.trim() || value.trim().toLowerCase() === "all") {
5510
+ return [...LOCAL_SKILL_SOURCES];
5511
+ }
5512
+ const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
5513
+ const invalid = requested.filter((source) => !LOCAL_SKILL_SOURCES.includes(source));
5514
+ if (invalid.length > 0) {
5515
+ throw new Error(`Unsupported local skill source: ${invalid.join(", ")}. Use ${LOCAL_SKILL_SOURCES.join(", ")}, or all.`);
5516
+ }
5517
+ return [...new Set(requested)];
5518
+ }
5519
+ function discoverLocalSkills(options = {}) {
5520
+ const cwd = options.cwd ?? process.cwd();
5521
+ const home = options.home ?? process.env.HOME ?? "";
5522
+ const sources = options.sources ?? [...LOCAL_SKILL_SOURCES];
5523
+ const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
5524
+ const context = options.context?.trim() ?? "";
5525
+ const roots = defaultRoots({ cwd, home });
5526
+ for (const [source, overrides] of Object.entries(options.roots ?? {})) {
5527
+ roots[source] = overrides;
5528
+ }
5529
+ const seen = /* @__PURE__ */ new Set();
5530
+ const candidates = [];
5531
+ for (const source of sources) {
5532
+ for (const root of roots[source] ?? []) {
5533
+ for (const path of walkSkillFiles(root)) {
5534
+ if (seen.has(path)) continue;
5535
+ seen.add(path);
5536
+ const text2 = readWindow(path, maxBytes);
5537
+ if (!text2 || /ORGX SKILL COMPOSED v1/.test(text2)) continue;
5538
+ const title = titleFrom(path, text2);
5539
+ const snippet = snippetFrom(text2);
5540
+ const scored = scoreCandidate(`${title}
5541
+ ${snippet}
5542
+ ${text2}`, context, source);
5543
+ candidates.push({
5544
+ agentDomains: inferDomains(`${title}
5545
+ ${snippet}`, context),
5546
+ id: `${source}-${hash(path)}`,
5547
+ path,
5548
+ reason: scored.reason,
5549
+ score: scored.score,
5550
+ snippet,
5551
+ source,
5552
+ title
5553
+ });
5554
+ }
5555
+ }
5556
+ }
5557
+ return candidates.sort((left, right) => right.score - left.score || left.title.localeCompare(right.title)).slice(0, Math.max(1, options.limit ?? DEFAULT_LIMIT));
5558
+ }
5559
+ function selectLocalSkillCandidates(candidates, selection) {
5560
+ const wanted = selection.split(",").map((item) => item.trim()).filter(Boolean);
5561
+ const selected = [];
5562
+ for (const item of wanted) {
5563
+ const byIndex = /^\d+$/.test(item) ? candidates[Number(item) - 1] : void 0;
5564
+ const byId = candidates.find((candidate) => candidate.id === item);
5565
+ const match = byIndex ?? byId;
5566
+ if (!match) {
5567
+ throw new Error(`No local skill candidate matched '${item}'.`);
5568
+ }
5569
+ selected.push(match);
5570
+ }
5571
+ return [...new Map(selected.map((candidate) => [candidate.id, candidate])).values()];
5572
+ }
5573
+ function buildLocalSkillExtensionContent(candidates, context) {
5574
+ const lines = [
5575
+ "# Local Skill Preferences",
5576
+ "",
5577
+ "Use these opt-in local preferences when they are relevant to the current OrgX initiative. Do not override explicit user instructions or repo guardrails."
5578
+ ];
5579
+ const trimmedContext = context?.trim();
5580
+ if (trimmedContext) {
5581
+ lines.push("", `Context: ${trimmedContext}`);
5582
+ }
5583
+ for (const candidate of candidates) {
5584
+ lines.push(
5585
+ "",
5586
+ `## ${candidate.title}`,
5587
+ "",
5588
+ `- Source: ${candidate.source} (${relative2(process.cwd(), candidate.path)})`,
5589
+ `- Suggested agents: ${candidate.agentDomains.join(", ")}`,
5590
+ `- Preserve: ${candidate.snippet || "local workflow preference from this skill file."}`
5591
+ );
5592
+ }
5593
+ return lines.join("\n");
5594
+ }
5595
+
5395
5596
  // src/lib/mutation-output.ts
5396
5597
  function summarizeMutationResults(results) {
5397
5598
  return results.map((result) => ({
@@ -5913,6 +6114,40 @@ function normalizeHotkey(input) {
5913
6114
  }
5914
6115
  }
5915
6116
 
6117
+ // src/lib/setup-profiles.ts
6118
+ var SETUP_PROFILES = {
6119
+ "local-ai-workflow": {
6120
+ id: "local-ai-workflow",
6121
+ label: "Local AI workflow",
6122
+ firstInitiativeTitle: "Make local AI work visible and shippable",
6123
+ firstInitiativeSummary: [
6124
+ "First OrgX initiative tailored for a local AI-assisted workflow.",
6125
+ "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."
6126
+ ].join(" "),
6127
+ 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.",
6128
+ localProofCommand: "orgx-wizard sessions reconcile --from opencode,github --public-share --yes",
6129
+ skillDiscoveryCommand: `orgx-wizard skills discover-local --from opencode,claude,codex,workspace --context "Describe this person's domain, tools, and workflow"`
6130
+ }
6131
+ };
6132
+ function resolveSetupProfile(id) {
6133
+ const normalized = id?.trim().toLowerCase();
6134
+ if (!normalized) return null;
6135
+ return SETUP_PROFILES[normalized] ?? null;
6136
+ }
6137
+ function supportedSetupProfileIds() {
6138
+ return Object.keys(SETUP_PROFILES).sort();
6139
+ }
6140
+ function buildProfileSummary(profile, context) {
6141
+ const trimmedContext = context?.trim();
6142
+ if (!trimmedContext) return profile.firstInitiativeSummary;
6143
+ return `${profile.firstInitiativeSummary} Context: ${trimmedContext}`;
6144
+ }
6145
+ function buildProfileHandoffPrompt(profile, fallbackPrompt, context) {
6146
+ if (!profile) return fallbackPrompt;
6147
+ const trimmedContext = context?.trim();
6148
+ return trimmedContext ? `${profile.handoffPrompt} Context: ${trimmedContext}` : profile.handoffPrompt;
6149
+ }
6150
+
5916
6151
  // src/lib/daily-brief-onboarding.ts
5917
6152
  function extractErrorHint(body) {
5918
6153
  if (!body) return null;
@@ -6222,8 +6457,8 @@ async function fetchOnboardingState(auth) {
6222
6457
  }
6223
6458
 
6224
6459
  // 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";
6460
+ import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
6461
+ import { basename as basename3, join as join5, relative as relative3 } from "path";
6227
6462
  var AI_SESSION_SOURCES = ["codex", "claude"];
6228
6463
  var DEFAULT_LIMIT_PER_SOURCE = 3;
6229
6464
  var DEFAULT_SINCE_DAYS = 30;
@@ -6376,7 +6611,7 @@ function keepAuditRelevantLines(text2) {
6376
6611
  return text2.split(/\r?\n/).map((line) => normalizeAuditRelevantLine(line)).filter((line) => Boolean(line));
6377
6612
  }
6378
6613
  function collectJsonlFiles(root, source) {
6379
- if (!existsSync5(root)) return [];
6614
+ if (!existsSync6(root)) return [];
6380
6615
  const files = [];
6381
6616
  const stack = [root];
6382
6617
  while (stack.length > 0) {
@@ -6384,15 +6619,15 @@ function collectJsonlFiles(root, source) {
6384
6619
  if (!current) continue;
6385
6620
  let entries;
6386
6621
  try {
6387
- entries = readdirSync3(current);
6622
+ entries = readdirSync4(current);
6388
6623
  } catch {
6389
6624
  continue;
6390
6625
  }
6391
6626
  for (const entry of entries) {
6392
- const path = join4(current, entry);
6627
+ const path = join5(current, entry);
6393
6628
  let stats;
6394
6629
  try {
6395
- stats = statSync3(path);
6630
+ stats = statSync4(path);
6396
6631
  } catch {
6397
6632
  continue;
6398
6633
  }
@@ -6410,13 +6645,13 @@ function collectJsonlFiles(root, source) {
6410
6645
  function readSessionImport(candidate, root, options) {
6411
6646
  let stats;
6412
6647
  try {
6413
- stats = statSync3(candidate.path);
6648
+ stats = statSync4(candidate.path);
6414
6649
  } catch {
6415
6650
  return null;
6416
6651
  }
6417
6652
  if (stats.size > options.maxBytesPerFile) return null;
6418
6653
  const extractor = candidate.source === "codex" ? extractCodexMessageText : extractClaudeMessageText;
6419
- const lines = readFileSync3(candidate.path, "utf8").split(/\r?\n/);
6654
+ const lines = readFileSync4(candidate.path, "utf8").split(/\r?\n/);
6420
6655
  const relevantLines = [];
6421
6656
  let messageCount = 0;
6422
6657
  for (const line of lines) {
@@ -6428,10 +6663,10 @@ function readSessionImport(candidate, root, options) {
6428
6663
  }
6429
6664
  const deduped = [...new Set(relevantLines)].slice(0, 80);
6430
6665
  if (deduped.length === 0) return null;
6431
- const relativePath = relative2(root, candidate.path);
6666
+ const relativePath = relative3(root, candidate.path);
6432
6667
  return {
6433
6668
  import: {
6434
- sourceId: `${candidate.source}:${basename2(candidate.path, ".jsonl")}`,
6669
+ sourceId: `${candidate.source}:${basename3(candidate.path, ".jsonl")}`,
6435
6670
  sourceLabel: `${candidate.source === "codex" ? "Codex" : "Claude"} session ${relativePath}`,
6436
6671
  metadata: {
6437
6672
  bytes: stats.size,
@@ -6501,11 +6736,11 @@ function loadAiSessionImports(options) {
6501
6736
  }
6502
6737
 
6503
6738
  // src/lib/work-graph-source-adapters.ts
6504
- import { createHash as createHash3 } from "crypto";
6739
+ import { createHash as createHash4 } from "crypto";
6505
6740
  import { execFileSync } from "child_process";
6506
- import { closeSync, existsSync as existsSync6, openSync, readFileSync as readFileSync4, readdirSync as readdirSync4, readSync, statSync as statSync4 } from "fs";
6741
+ import { closeSync, existsSync as existsSync7, openSync, readFileSync as readFileSync5, readdirSync as readdirSync5, readSync, statSync as statSync5 } from "fs";
6507
6742
  import { homedir as homedir2 } from "os";
6508
- import { basename as basename3, join as join5, resolve } from "path";
6743
+ import { basename as basename4, join as join6, resolve } from "path";
6509
6744
  var CLIENT_SOURCES = ["claude_code", "codex", "opencode", "goose", "cursor", "github", "slack"];
6510
6745
  var DEFAULT_LIMIT_PER_SOURCE2 = 8;
6511
6746
  var DEFAULT_SINCE_DAYS2 = 45;
@@ -6524,21 +6759,21 @@ function parseInvestigationSourceList(value) {
6524
6759
  }
6525
6760
  return deduped;
6526
6761
  }
6527
- function hash(value, length = 24) {
6528
- return createHash3("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
6762
+ function hash2(value, length = 24) {
6763
+ return createHash4("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
6529
6764
  }
6530
6765
  function expandPath(path, env) {
6531
6766
  return path.replace(/^~(?=\/|$)/, env.home).replace(/\$CWD/g, env.cwd).replace(/\$HOME/g, env.home);
6532
6767
  }
6533
- function safeStat(path) {
6768
+ function safeStat2(path) {
6534
6769
  try {
6535
- return statSync4(path);
6770
+ return statSync5(path);
6536
6771
  } catch {
6537
6772
  return null;
6538
6773
  }
6539
6774
  }
6540
6775
  function walkFiles(root, predicate, maxFiles = 500) {
6541
- if (!existsSync6(root)) return [];
6776
+ if (!existsSync7(root)) return [];
6542
6777
  const files = [];
6543
6778
  const stack = [root];
6544
6779
  const ignored = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist", "build", ".turbo"]);
@@ -6547,14 +6782,14 @@ function walkFiles(root, predicate, maxFiles = 500) {
6547
6782
  if (!current) continue;
6548
6783
  let entries;
6549
6784
  try {
6550
- entries = readdirSync4(current);
6785
+ entries = readdirSync5(current);
6551
6786
  } catch {
6552
6787
  continue;
6553
6788
  }
6554
6789
  for (const entry of entries) {
6555
6790
  if (ignored.has(entry)) continue;
6556
- const path = join5(current, entry);
6557
- const stats = safeStat(path);
6791
+ const path = join6(current, entry);
6792
+ const stats = safeStat2(path);
6558
6793
  if (!stats) continue;
6559
6794
  if (stats.isDirectory()) {
6560
6795
  stack.push(path);
@@ -6606,10 +6841,10 @@ function nowWindow(timestamp, now) {
6606
6841
  return "older";
6607
6842
  }
6608
6843
  function makeRawEvent(input) {
6609
- const contentHash = hash(input.payload, 64);
6844
+ const contentHash = hash2(input.payload, 64);
6610
6845
  const uri = `${input.client}:${input.sessionId}:${input.uriSuffix}`;
6611
6846
  return {
6612
- event_id: `evt_${hash([uri, contentHash], 24)}`,
6847
+ event_id: `evt_${hash2([uri, contentHash], 24)}`,
6613
6848
  source_ref: {
6614
6849
  source_id: input.client,
6615
6850
  uri,
@@ -6652,7 +6887,7 @@ function parseJsonLine2(line) {
6652
6887
  }
6653
6888
  function readTextWindow(path, stats, maxBytes) {
6654
6889
  if (stats.size <= maxBytes) {
6655
- return { text: readFileSync4(path, "utf8"), truncated: false };
6890
+ return { text: readFileSync5(path, "utf8"), truncated: false };
6656
6891
  }
6657
6892
  const bytesToRead = Math.min(stats.size, maxBytes);
6658
6893
  const buffer = Buffer.alloc(bytesToRead);
@@ -6796,7 +7031,7 @@ function mapClaudeRecord(record, fallbackTimestamp) {
6796
7031
  return out;
6797
7032
  }
6798
7033
  function readJsonlCandidate(candidate, options) {
6799
- const stats = safeStat(candidate.path);
7034
+ const stats = safeStat2(candidate.path);
6800
7035
  if (!stats) {
6801
7036
  return {
6802
7037
  collectionMethods: [],
@@ -6810,7 +7045,7 @@ function readJsonlCandidate(candidate, options) {
6810
7045
  const window = readTextWindow(candidate.path, stats, options.maxBytesPerFile);
6811
7046
  const lines = window.text.split(/\r?\n/);
6812
7047
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
6813
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
7048
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6814
7049
  const events = [];
6815
7050
  for (const [index, line] of lines.entries()) {
6816
7051
  if (!line.trim()) continue;
@@ -6873,7 +7108,7 @@ function mapGenericJsonRecord(record, fallbackTimestamp) {
6873
7108
  }];
6874
7109
  }
6875
7110
  function readJsonCandidate(candidate, options) {
6876
- const stats = safeStat(candidate.path);
7111
+ const stats = safeStat2(candidate.path);
6877
7112
  if (!stats || stats.size > options.maxBytesPerFile) {
6878
7113
  return {
6879
7114
  collectionMethods: [],
@@ -6886,7 +7121,7 @@ function readJsonCandidate(candidate, options) {
6886
7121
  }
6887
7122
  let parsed;
6888
7123
  try {
6889
- parsed = JSON.parse(readFileSync4(candidate.path, "utf8"));
7124
+ parsed = JSON.parse(readFileSync5(candidate.path, "utf8"));
6890
7125
  } catch {
6891
7126
  return {
6892
7127
  collectionMethods: [],
@@ -6899,7 +7134,7 @@ function readJsonCandidate(candidate, options) {
6899
7134
  }
6900
7135
  const records = flattenPotentialMessages(parsed).slice(0, 800);
6901
7136
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
6902
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
7137
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6903
7138
  const events = records.flatMap(
6904
7139
  (record, index) => mapGenericJsonRecord(record, fallbackTimestamp).map(
6905
7140
  (item, partIndex) => makeRawEvent({
@@ -6952,7 +7187,7 @@ function flattenPotentialMessages(value) {
6952
7187
  return out;
6953
7188
  }
6954
7189
  function readMarkdownCandidate(candidate, options) {
6955
- const stats = safeStat(candidate.path);
7190
+ const stats = safeStat2(candidate.path);
6956
7191
  if (!stats || stats.size > options.maxBytesPerFile) {
6957
7192
  return {
6958
7193
  collectionMethods: [],
@@ -6963,8 +7198,8 @@ function readMarkdownCandidate(candidate, options) {
6963
7198
  searchedSessions: 0
6964
7199
  };
6965
7200
  }
6966
- const text2 = readFileSync4(candidate.path, "utf8");
6967
- const sessionId = basename3(candidate.path).replace(/\.[^.]+$/, "");
7201
+ const text2 = readFileSync5(candidate.path, "utf8");
7202
+ const sessionId = basename4(candidate.path).replace(/\.[^.]+$/, "");
6968
7203
  const timestamp = new Date(stats.mtimeMs).toISOString();
6969
7204
  const event = makeRawEvent({
6970
7205
  client: candidate.source,
@@ -6993,7 +7228,7 @@ function readMarkdownCandidate(candidate, options) {
6993
7228
  };
6994
7229
  }
6995
7230
  function readGitReflogCandidate(candidate, options) {
6996
- const stats = safeStat(candidate.path);
7231
+ const stats = safeStat2(candidate.path);
6997
7232
  if (!stats || stats.size > options.maxBytesPerFile) {
6998
7233
  return {
6999
7234
  collectionMethods: [],
@@ -7004,7 +7239,7 @@ function readGitReflogCandidate(candidate, options) {
7004
7239
  searchedSessions: 0
7005
7240
  };
7006
7241
  }
7007
- const lines = readFileSync4(candidate.path, "utf8").split(/\r?\n/).filter(Boolean).slice(-250);
7242
+ const lines = readFileSync5(candidate.path, "utf8").split(/\r?\n/).filter(Boolean).slice(-250);
7008
7243
  const events = [];
7009
7244
  const fallbackTimestamp = new Date(stats.mtimeMs).toISOString();
7010
7245
  for (const [index, line] of lines.entries()) {
@@ -7150,10 +7385,10 @@ function discoverCandidates(source, env, sinceMs) {
7150
7385
  for (const rootPattern of paths) {
7151
7386
  const expanded = expandPath(rootPattern, env);
7152
7387
  const root = expanded.includes("*") ? expanded.slice(0, expanded.indexOf("*")).replace(/\/$/, "") : expanded;
7153
- const exactStats = safeStat(expanded);
7388
+ const exactStats = safeStat2(expanded);
7154
7389
  const found = exactStats?.isFile() ? [expanded] : walkFiles(root, predicate);
7155
7390
  for (const path of found) {
7156
- const stats = safeStat(path);
7391
+ const stats = safeStat2(path);
7157
7392
  if (!stats || stats.mtimeMs < sinceMs) continue;
7158
7393
  candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
7159
7394
  }
@@ -7221,7 +7456,7 @@ function discoverOverrideCandidates(source, root, sinceMs) {
7221
7456
  const files = walkFiles(root, (path) => path.endsWith(".jsonl") || path.endsWith(".json"));
7222
7457
  const candidates = [];
7223
7458
  for (const path of files) {
7224
- const stats = safeStat(path);
7459
+ const stats = safeStat2(path);
7225
7460
  if (!stats || stats.mtimeMs < sinceMs) continue;
7226
7461
  candidates.push({ extractionMode, mtimeMs: stats.mtimeMs, path, source });
7227
7462
  }
@@ -7296,7 +7531,7 @@ function extractionFromEvents(input) {
7296
7531
  }
7297
7532
  return {
7298
7533
  schema_version: "2.0.0.investigation",
7299
- extraction_id: `${input.client}:investigation:${hash([input.client, input.events.map((event) => event.event_id)], 12)}`,
7534
+ extraction_id: `${input.client}:investigation:${hash2([input.client, input.events.map((event) => event.event_id)], 12)}`,
7300
7535
  source_client: workGraphSourceClient(input.client),
7301
7536
  source_label: clientLabel(input.client),
7302
7537
  collection_methods: [...new Set(input.collectionMethods)].sort(),
@@ -7389,7 +7624,7 @@ function loadWorkGraphInvestigationSourceData(options) {
7389
7624
  }
7390
7625
 
7391
7626
  // src/lib/self-audit.ts
7392
- import { createHash as createHash4 } from "crypto";
7627
+ import { createHash as createHash5 } from "crypto";
7393
7628
  var SELF_AUDIT_SCHEMA_VERSION = "2026-04-27";
7394
7629
  var AUDIT_DIMENSIONS = [
7395
7630
  "queryability",
@@ -7556,7 +7791,7 @@ function buildSelfCritique(scores) {
7556
7791
  });
7557
7792
  }
7558
7793
  function hashPlanPayload(payload) {
7559
- return createHash4("sha256").update(JSON.stringify(payload)).digest("hex");
7794
+ return createHash5("sha256").update(JSON.stringify(payload)).digest("hex");
7560
7795
  }
7561
7796
  function buildSelfAuditPlan(input) {
7562
7797
  if (input.imports.length === 0) {
@@ -7886,10 +8121,10 @@ Rollback: ${plan.recommended_follow_up.rollback}`,
7886
8121
  }
7887
8122
 
7888
8123
  // src/lib/work-graph.ts
7889
- import { createHash as createHash6 } from "crypto";
8124
+ import { createHash as createHash7 } from "crypto";
7890
8125
 
7891
8126
  // src/lib/work-graph-investigation.ts
7892
- import { createHash as createHash5 } from "crypto";
8127
+ import { createHash as createHash6 } from "crypto";
7893
8128
  var WORK_GRAPH_INVESTIGATION_SCHEMA_VERSION = "2.0.0";
7894
8129
  var WORK_GRAPH_INVESTIGATION_CLIENTS = [
7895
8130
  "claude_code",
@@ -8021,8 +8256,8 @@ var CAPABILITY_CEILINGS = {
8021
8256
  decision_lineage: "high"
8022
8257
  }
8023
8258
  };
8024
- function hash2(value, length = 16) {
8025
- return createHash5("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
8259
+ function hash3(value, length = 16) {
8260
+ return createHash6("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
8026
8261
  }
8027
8262
  function clamp(value, min = 0, max = 1) {
8028
8263
  return Math.max(min, Math.min(max, value));
@@ -8075,8 +8310,8 @@ function loopTopicKey(loop) {
8075
8310
  for (const [pattern, topic] of topicRules) {
8076
8311
  if (pattern.test(text2)) return topic;
8077
8312
  }
8078
- if (!intent) return `unclassified:${hash2(loop.loop_id, 10)}`;
8079
- return `topic:${hash2(intent.toLowerCase(), 10)}`;
8313
+ if (!intent) return `unclassified:${hash3(loop.loop_id, 10)}`;
8314
+ return `topic:${hash3(intent.toLowerCase(), 10)}`;
8080
8315
  }
8081
8316
  function bestFamilyCentroid(group) {
8082
8317
  const candidates = group.map((loop) => cleanLoopIntent(loop.origin.intent, "")).filter(Boolean).map((intent) => {
@@ -8140,7 +8375,7 @@ function windowFor(timestamp, now) {
8140
8375
  function rawEventFromFinding(finding, index, generatedAt) {
8141
8376
  const sourceClient = normalizeClient(finding.source_client);
8142
8377
  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)}`;
8378
+ const eventId = `evt_${hash3([finding.evidence_ref, finding.title, index], 24)}`;
8144
8379
  const payload = {
8145
8380
  title: finding.title,
8146
8381
  summary: finding.summary,
@@ -8172,7 +8407,7 @@ function rawEventFromFinding(finding, index, generatedAt) {
8172
8407
  payload,
8173
8408
  raw_byte_offset: null,
8174
8409
  raw_row_id: null,
8175
- content_hash: hash2(payload, 64),
8410
+ content_hash: hash3(payload, 64),
8176
8411
  redaction_applied: true
8177
8412
  };
8178
8413
  }
@@ -8184,7 +8419,7 @@ function rawEventFromSourceEvent(event, index, generatedAt) {
8184
8419
  text_summary: event.text.slice(0, 420)
8185
8420
  };
8186
8421
  return {
8187
- event_id: `evt_${hash2([event.evidence_ref, index], 24)}`,
8422
+ event_id: `evt_${hash3([event.evidence_ref, index], 24)}`,
8188
8423
  source_ref: {
8189
8424
  source_id: sourceClient,
8190
8425
  uri: event.evidence_ref,
@@ -8202,7 +8437,7 @@ function rawEventFromSourceEvent(event, index, generatedAt) {
8202
8437
  payload,
8203
8438
  raw_byte_offset: null,
8204
8439
  raw_row_id: null,
8205
- content_hash: hash2(payload, 64),
8440
+ content_hash: hash3(payload, 64),
8206
8441
  redaction_applied: true
8207
8442
  };
8208
8443
  }
@@ -8333,7 +8568,7 @@ function buildWorkLoops(input) {
8333
8568
  trail.confidence || matchedFindings.reduce((total, finding) => total + finding.confidence, 0) / Math.max(1, matchedFindings.length)
8334
8569
  );
8335
8570
  return {
8336
- loop_id: `loop_${hash2([trail.id, index], 18)}`,
8571
+ loop_id: `loop_${hash3([trail.id, index], 18)}`,
8337
8572
  cites: eventIds,
8338
8573
  origin: {
8339
8574
  event_id: eventIds[0] ?? `evt_missing_${index}`,
@@ -8419,7 +8654,7 @@ function buildLoopFamilies(loops, events, impact) {
8419
8654
  (loop) => loop.cites.map((cite) => events.find((event) => event.event_id === cite)?.source_id).filter(Boolean)
8420
8655
  )
8421
8656
  );
8422
- const familyId = `family_${hash2(key, 16)}`;
8657
+ const familyId = `family_${hash3(key, 16)}`;
8423
8658
  const timestamps = group.map((loop) => Date.parse(loop.origin.timestamp)).filter(Number.isFinite);
8424
8659
  const spanDays = timestamps.length > 1 ? Math.max(1, Math.ceil((Math.max(...timestamps) - Math.min(...timestamps)) / 864e5)) : 0;
8425
8660
  const publicLoopCount = group.filter((loop) => loop.public_surface).length;
@@ -8772,7 +9007,7 @@ function counterfactualForLoop(loop, family) {
8772
9007
  orgx_outcome: repair.expected_outcome,
8773
9008
  resulting_entity: {
8774
9009
  kind: entityKind,
8775
- inferred_id: `orgx_${entityKind}_${hash2(loop.loop_id, 12)}`,
9010
+ inferred_id: `orgx_${entityKind}_${hash3(loop.loop_id, 12)}`,
8776
9011
  would_link_to: loop.cites
8777
9012
  },
8778
9013
  realism_score: Number(realism.toFixed(2)),
@@ -8983,7 +9218,7 @@ function buildMirror(input) {
8983
9218
  };
8984
9219
  }
8985
9220
  function buildWorkGraphInvestigation(input) {
8986
- const auditId = `wgi_${hash2([input.fingerprint, input.generatedAt], 24)}`;
9221
+ const auditId = `wgi_${hash3([input.fingerprint, input.generatedAt], 24)}`;
8987
9222
  const rawEvents = buildRawEvents(input);
8988
9223
  const corpus = buildCorpusManifest({
8989
9224
  auditId,
@@ -9124,7 +9359,7 @@ function clampScore2(value) {
9124
9359
  return Math.max(0, Math.min(100, Math.round(value)));
9125
9360
  }
9126
9361
  function hashJson(value) {
9127
- return createHash6("sha256").update(JSON.stringify(value)).digest("hex");
9362
+ return createHash7("sha256").update(JSON.stringify(value)).digest("hex");
9128
9363
  }
9129
9364
  function normalizeFingerprintText(value) {
9130
9365
  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 +12450,14 @@ function renderWorkGraphMarkdown(report, options = {}) {
12215
12450
  }
12216
12451
 
12217
12452
  // src/lib/work-graph-publish.ts
12218
- import { createHash as createHash7, randomUUID as randomUUID2 } from "crypto";
12453
+ import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
12219
12454
  import { gzipSync } from "zlib";
12220
12455
  var WORK_GRAPH_REPORT_GZIP_THRESHOLD_BYTES = 4e6;
12221
12456
  var WORK_GRAPH_REPORT_CHUNK_UPLOAD_THRESHOLD_BYTES = 5e5;
12222
12457
  var WORK_GRAPH_REPORT_CHUNK_CHARS = 3e4;
12223
12458
  var WORK_GRAPH_REPORT_CHUNK_UPLOAD_TIMEOUT_MS = 3e5;
12224
12459
  function hashText(value) {
12225
- return createHash7("sha256").update(value).digest("hex");
12460
+ return createHash8("sha256").update(value).digest("hex");
12226
12461
  }
12227
12462
  function buildWorkGraphReportPostPayload(report, options = {}) {
12228
12463
  return {
@@ -12429,8 +12664,8 @@ async function publishWorkGraphEvents(fingerprint, patch, options = {}) {
12429
12664
  }
12430
12665
 
12431
12666
  // src/lib/work-graph-hook-events.ts
12432
- import { createHash as createHash8 } from "crypto";
12433
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
12667
+ import { createHash as createHash9 } from "crypto";
12668
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
12434
12669
  var SOURCE_CLIENTS = [
12435
12670
  "codex",
12436
12671
  "claude",
@@ -12464,7 +12699,7 @@ function asStringArray(value) {
12464
12699
  return value.filter((item) => typeof item === "string" && item.trim().length > 0);
12465
12700
  }
12466
12701
  function stableHash(value) {
12467
- return createHash8("sha256").update(value).digest("hex").slice(0, 20);
12702
+ return createHash9("sha256").update(value).digest("hex").slice(0, 20);
12468
12703
  }
12469
12704
  function normalizeSourceClient2(value) {
12470
12705
  const raw = asString2(value)?.toLowerCase();
@@ -12531,8 +12766,8 @@ function readHookRecord(line) {
12531
12766
  }
12532
12767
  }
12533
12768
  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);
12769
+ if (!existsSync8(path)) return { path, records: [], skipped: 0 };
12770
+ const lines = readFileSync6(path, "utf8").split(/\r?\n/).filter((line) => line.trim().length > 0);
12536
12771
  const selected = lines.slice(Math.max(0, lines.length - Math.max(1, limit)));
12537
12772
  const records = [];
12538
12773
  let skipped = Math.max(0, lines.length - selected.length);
@@ -12668,20 +12903,20 @@ function buildWorkGraphHookReplayPatch(readResult) {
12668
12903
  }
12669
12904
 
12670
12905
  // src/lib/runtime-hooks.ts
12671
- import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
12906
+ import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
12672
12907
  import { homedir as homedir3 } from "os";
12673
- import { dirname as dirname4, join as join6 } from "path";
12908
+ import { dirname as dirname4, join as join7 } from "path";
12674
12909
  var HOOK_MARKER = "orgx-session-hook.mjs";
12675
12910
  var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
12676
12911
  var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
12677
12912
  function defaultPaths(options = {}) {
12678
- const hookDir = join6(ORGX_WIZARD_CONFIG_HOME, "hooks");
12913
+ const hookDir = join7(ORGX_WIZARD_CONFIG_HOME, "hooks");
12679
12914
  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")
12915
+ claudeSettingsPath: options.claudeSettingsPath ?? join7(CLAUDE_DIR, "settings.json"),
12916
+ codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join7(CODEX_DIR, "config.toml"),
12917
+ codexHooksPath: options.codexHooksPath ?? join7(CODEX_DIR, "hooks.json"),
12918
+ hookScriptPath: options.hookScriptPath ?? join7(hookDir, HOOK_MARKER),
12919
+ outboxPath: options.outboxPath ?? join7(hookDir, "events.jsonl")
12685
12920
  };
12686
12921
  }
12687
12922
  function countJsonlLines(path) {
@@ -12694,7 +12929,7 @@ function backupPath(path, now) {
12694
12929
  return `${path}.bak.${timestamp}`;
12695
12930
  }
12696
12931
  function backupExisting(path, now) {
12697
- if (!existsSync8(path)) return null;
12932
+ if (!existsSync9(path)) return null;
12698
12933
  const backup = backupPath(path, now);
12699
12934
  copyFileSync(path, backup);
12700
12935
  return backup;
@@ -12892,7 +13127,7 @@ function inspectRuntimeHooks(options = {}) {
12892
13127
  installed: {
12893
13128
  claudeCode: hasOrgxHook(claudeSettingsRaw),
12894
13129
  codex: hasOrgxHook(codexHooksRaw),
12895
- hookScript: existsSync8(paths.hookScriptPath)
13130
+ hookScript: existsSync9(paths.hookScriptPath)
12896
13131
  },
12897
13132
  codex: {
12898
13133
  configExists: Boolean(codexConfigRaw),
@@ -13154,10 +13389,10 @@ async function runHookReplayCommand(options) {
13154
13389
  }
13155
13390
  function readAuditInput(options, interactive) {
13156
13391
  if (options.input?.trim()) {
13157
- return readFileSync7(resolve2(options.input.trim()), "utf8");
13392
+ return readFileSync8(resolve2(options.input.trim()), "utf8");
13158
13393
  }
13159
13394
  if (!process.stdin.isTTY) {
13160
- return readFileSync7(0, "utf8");
13395
+ return readFileSync8(0, "utf8");
13161
13396
  }
13162
13397
  if (!interactive) {
13163
13398
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -13190,7 +13425,7 @@ function collectPathOption(value, previous = []) {
13190
13425
  }
13191
13426
  function parseClientExtractionFile(path) {
13192
13427
  const resolvedPath = resolve2(path);
13193
- const parsed = JSON.parse(readFileSync7(resolvedPath, "utf8"));
13428
+ const parsed = JSON.parse(readFileSync8(resolvedPath, "utf8"));
13194
13429
  if (!isRecord(parsed)) {
13195
13430
  throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
13196
13431
  }
@@ -13675,6 +13910,40 @@ function printSkillExtensions(extensions) {
13675
13910
  );
13676
13911
  }
13677
13912
  }
13913
+ function printLocalSkillCandidates(candidates) {
13914
+ if (candidates.length === 0) {
13915
+ console.log(` ${ICON.skip} ${pc3.dim("no local skill files found")}`);
13916
+ return;
13917
+ }
13918
+ candidates.forEach((candidate, index) => {
13919
+ const domains = candidate.agentDomains.join(", ");
13920
+ console.log(
13921
+ ` ${ICON.ok} ${pc3.bold(String(index + 1).padStart(2, " "))} ${pc3.bold(candidate.title)} ${pc3.dim(candidate.id)}`
13922
+ );
13923
+ console.log(` ${pc3.dim(`${candidate.source} \xB7 ${domains} \xB7 ${candidate.reason}`)}`);
13924
+ console.log(` ${pc3.dim(candidate.path)}`);
13925
+ if (candidate.snippet) {
13926
+ console.log(` ${pc3.dim(candidate.snippet)}`);
13927
+ }
13928
+ });
13929
+ }
13930
+ function writeLocalSkillSelection(input) {
13931
+ const scope = input.scope ?? "user";
13932
+ const nextContent = buildLocalSkillExtensionContent(input.candidates, input.context);
13933
+ const existing = listSkillExtensions().find(
13934
+ (extension) => extension.skillId === "orgx" && extension.scope === scope
13935
+ );
13936
+ const content = existing?.content.trim() ? `${existing.content.trimEnd()}
13937
+
13938
+ ${nextContent}` : nextContent;
13939
+ return addSkillExtension({
13940
+ content,
13941
+ overwrite: true,
13942
+ scope,
13943
+ skillId: "orgx",
13944
+ title: "Local skill preferences"
13945
+ });
13946
+ }
13678
13947
  function printSkillExtensionWrite(result) {
13679
13948
  const action = result.created ? "created" : result.changed ? "updated" : "unchanged";
13680
13949
  const color = result.created || result.changed ? pc3.green : pc3.dim;
@@ -13740,6 +14009,8 @@ function printSetupScopeNote() {
13740
14009
  }
13741
14010
  function printFirstValueHandoff(input) {
13742
14011
  const cmd = getCmd();
14012
+ const fallbackPrompt = `Use OrgX to continue "${input.initiative.initiative.title}" in ${input.workspace.name}. Show the next action, then start with the onboarding task.`;
14013
+ const handoffPrompt = buildProfileHandoffPrompt(input.profile, fallbackPrompt, input.context);
13743
14014
  console.log("");
13744
14015
  console.log(pc3.bold("first OrgX handoff"));
13745
14016
  console.log(
@@ -13748,9 +14019,13 @@ function printFirstValueHandoff(input) {
13748
14019
  console.log(` live: ${input.initiative.liveUrl}`);
13749
14020
  console.log(
13750
14021
  ` ${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.`
14022
+ handoffPrompt
13752
14023
  )}`
13753
14024
  );
14025
+ if (input.profile) {
14026
+ console.log(` ${pc3.dim("local proof:")} ${pc3.cyan(input.profile.localProofCommand)}`);
14027
+ console.log(` ${pc3.dim("skills:")} ${pc3.cyan(input.profile.skillDiscoveryCommand)}`);
14028
+ }
13754
14029
  console.log(` ${pc3.dim("later:")} ${pc3.cyan(`${cmd} doctor`)} ${pc3.dim("checks tool wiring")}`);
13755
14030
  }
13756
14031
  function firstValueRecordToResult(record) {
@@ -14240,7 +14515,7 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14240
14515
  }
14241
14516
  if (firstInitiativeChoice === "yes") {
14242
14517
  const title = await textPrompt({
14243
- initialValue: FIRST_VALUE_INITIATIVE_TITLE,
14518
+ initialValue: input.profile?.firstInitiativeTitle ?? FIRST_VALUE_INITIATIVE_TITLE,
14244
14519
  message: "What should OrgX help you move forward first?",
14245
14520
  validate: (value) => {
14246
14521
  if (!value || value.trim().length === 0) {
@@ -14257,6 +14532,7 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14257
14532
  spinner.start();
14258
14533
  try {
14259
14534
  firstValueInitiative = await ensureFirstValueInitiative(input.workspace, {
14535
+ ...input.context || input.profile ? { summary: input.profile ? buildProfileSummary(input.profile, input.context) : input.context } : {},
14260
14536
  title: String(title)
14261
14537
  });
14262
14538
  spinner.succeed(
@@ -14347,7 +14623,9 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14347
14623
  }
14348
14624
  if (firstValueInitiative) {
14349
14625
  printFirstValueHandoff({
14626
+ ...input.context ? { context: input.context } : {},
14350
14627
  initiative: firstValueInitiative,
14628
+ ...input.profile !== void 0 ? { profile: input.profile } : {},
14351
14629
  workspace: input.workspace
14352
14630
  });
14353
14631
  }
@@ -14420,6 +14698,64 @@ async function maybeConfigureOptionalWorkspaceAddOns(input) {
14420
14698
  });
14421
14699
  }
14422
14700
  }
14701
+ const localSkillDiscoverySkipped = hasSetupPromptSkip(
14702
+ input.workspace.id,
14703
+ "local_skill_discovery"
14704
+ );
14705
+ if (!localSkillDiscoverySkipped) {
14706
+ const context = input.context?.trim();
14707
+ const candidates = discoverLocalSkills({
14708
+ ...context ? { context } : {},
14709
+ limit: 8
14710
+ });
14711
+ if (candidates.length > 0) {
14712
+ const selectedSkillIds = await multiselectPrompt({
14713
+ message: "Bring existing local skills into your OrgX agents?",
14714
+ options: [
14715
+ ...candidates.map((candidate, index) => ({
14716
+ value: candidate.id,
14717
+ label: `${index + 1}. ${candidate.title}`,
14718
+ hint: `${candidate.source} \xB7 ${candidate.agentDomains.join(", ")} \xB7 ${candidate.reason}`
14719
+ })),
14720
+ {
14721
+ value: "__skip__",
14722
+ label: "Skip local skill import",
14723
+ hint: "You can run `orgx-wizard skills discover-local` later."
14724
+ }
14725
+ ],
14726
+ required: false
14727
+ });
14728
+ if (clack.isCancel(selectedSkillIds)) {
14729
+ clack.cancel("Setup cancelled.");
14730
+ return "cancelled";
14731
+ }
14732
+ const selected = candidates.filter((candidate) => selectedSkillIds.includes(candidate.id));
14733
+ if (selectedSkillIds.includes("__skip__") || selected.length === 0) {
14734
+ recordSetupPromptSkip({
14735
+ promptKey: "local_skill_discovery",
14736
+ workspaceId: input.workspace.id,
14737
+ workspaceName: input.workspace.name
14738
+ });
14739
+ console.log(` ${ICON.skip} ${pc3.dim("local skills skipped")}`);
14740
+ } else {
14741
+ const result = writeLocalSkillSelection({
14742
+ candidates: selected,
14743
+ ...context ? { context } : {}
14744
+ });
14745
+ printSkillExtensionWrite(result);
14746
+ console.log(
14747
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply selected local preferences to configured tools.`)}`
14748
+ );
14749
+ await safeTrackWizardTelemetry("local_skills_configured", {
14750
+ candidate_count: candidates.length,
14751
+ command: input.telemetry?.command ?? "setup",
14752
+ selected_count: selected.length,
14753
+ ...input.telemetry?.preset ? { preset: input.telemetry.preset } : {},
14754
+ profile: input.profile?.id ?? "none"
14755
+ });
14756
+ }
14757
+ }
14758
+ }
14423
14759
  return "configured";
14424
14760
  }
14425
14761
  async function promptOptionalCompanionPluginTargets(input) {
@@ -14573,20 +14909,26 @@ function printDoctorReport(report, assessment) {
14573
14909
  async function main() {
14574
14910
  const program = new Command();
14575
14911
  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;
14912
+ const pkgVersion = true ? "0.1.46" : void 0;
14577
14913
  program.version(pkgVersion ?? "unknown", "-V, --version");
14578
14914
  program.hook("preAction", (_thisCommand, actionCommand) => {
14579
14915
  if (Boolean(actionCommand.optsWithGlobals().json)) return;
14580
14916
  console.log(renderBanner(pkgVersion));
14581
14917
  });
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) => {
14918
+ 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
14919
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
14584
14920
  if (options.dailyBrief && options.skipDailyBrief) {
14585
14921
  throw new Error("Use either --daily-brief or --skip-daily-brief, not both.");
14586
14922
  }
14923
+ const setupProfile = resolveSetupProfile(options.profile);
14924
+ if (options.profile && !setupProfile) {
14925
+ throw new Error(`Unknown setup profile '${options.profile}'. Supported profiles: ${supportedSetupProfileIds().join(", ")}.`);
14926
+ }
14927
+ const setupContext = options.context?.trim() || void 0;
14587
14928
  await safeTrackWizardTelemetry("wizard_started", {
14588
14929
  command: "setup",
14589
14930
  interactive,
14931
+ profile: setupProfile?.id ?? "none",
14590
14932
  preset: options.preset ?? "standard"
14591
14933
  });
14592
14934
  printSetupScopeNote();
@@ -14656,7 +14998,9 @@ async function main() {
14656
14998
  );
14657
14999
  }
14658
15000
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
15001
+ ...setupContext ? { context: setupContext } : {},
14659
15002
  interactive,
15003
+ profile: setupProfile,
14660
15004
  telemetry: { command: "setup", preset: "founder" },
14661
15005
  workspace: presetResult.workspace,
14662
15006
  ...presetResult.demoInitiative ? { initiativeId: presetResult.demoInitiative.initiative.id } : {}
@@ -14817,7 +15161,9 @@ async function main() {
14817
15161
  return;
14818
15162
  }
14819
15163
  const addOnResult = await maybeConfigureOptionalWorkspaceAddOns({
15164
+ ...setupContext ? { context: setupContext } : {},
14820
15165
  interactive,
15166
+ profile: setupProfile,
14821
15167
  telemetry: { command: "setup", preset: "standard" },
14822
15168
  workspace: resolvedWorkspace
14823
15169
  });
@@ -15421,6 +15767,48 @@ async function main() {
15421
15767
  }
15422
15768
  }
15423
15769
  });
15770
+ 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) => {
15771
+ const sources = parseLocalSkillSources(options.from);
15772
+ const limit = parsePositiveInteger(options.limit, 12, "--limit");
15773
+ const candidates = discoverLocalSkills({
15774
+ ...options.context?.trim() ? { context: options.context.trim() } : {},
15775
+ limit,
15776
+ sources
15777
+ });
15778
+ let extensionWrite = null;
15779
+ if (options.apply?.trim()) {
15780
+ const selected = selectLocalSkillCandidates(candidates, options.apply);
15781
+ extensionWrite = writeLocalSkillSelection({
15782
+ candidates: selected,
15783
+ ...options.context?.trim() ? { context: options.context.trim() } : {},
15784
+ scope: options.scope ?? "user"
15785
+ });
15786
+ }
15787
+ if (options.json) {
15788
+ console.log(JSON.stringify({
15789
+ applied: extensionWrite ? {
15790
+ changed: extensionWrite.changed,
15791
+ created: extensionWrite.created,
15792
+ path: extensionWrite.path
15793
+ } : null,
15794
+ candidates
15795
+ }, null, 2));
15796
+ return;
15797
+ }
15798
+ printLocalSkillCandidates(candidates);
15799
+ if (extensionWrite) {
15800
+ console.log("");
15801
+ printSkillExtensionWrite(extensionWrite);
15802
+ console.log(
15803
+ ` ${ICON.skip} ${pc3.dim(`Run ${getCmd()} skills sync to apply selected local preferences to configured tools.`)}`
15804
+ );
15805
+ } else if (candidates.length > 0) {
15806
+ console.log("");
15807
+ console.log(
15808
+ ` ${ICON.skip} ${pc3.dim(`Opt in with ${getCmd()} skills discover-local --apply 1,2, then run ${getCmd()} skills sync.`)}`
15809
+ );
15810
+ }
15811
+ });
15424
15812
  const skillExtensions = skills.command("extensions").description("Create, edit, and sync user extensions appended after OrgX core skills.");
15425
15813
  skillExtensions.command("list").description("List local OrgX skill extensions.").action(() => {
15426
15814
  printSkillExtensions(listSkillExtensions());