@useorgx/wizard 0.1.26 → 0.1.27

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 readFileSync4 } from "fs";
6
+ import { readFileSync as readFileSync5 } from "fs";
7
7
  import { hostname } from "os";
8
8
  import { resolve } from "path";
9
9
  import { Command } from "commander";
@@ -6228,7 +6228,7 @@ var AI_SESSION_SOURCES = ["codex", "claude"];
6228
6228
  var DEFAULT_LIMIT_PER_SOURCE = 3;
6229
6229
  var DEFAULT_SINCE_DAYS = 30;
6230
6230
  var DEFAULT_MAX_BYTES_PER_FILE = 1e6;
6231
- var AUDIT_RELEVANT_LINE_PATTERN = /\b(decision|decided|artifact|receipt|proof|commitment|committed|next action|follow[- ]?up|outcome|result|impact|roi|economics|token|cost|saved|open loop|gap|blocker|risk|owner|dri|writeback|rollback|quality score)\b/i;
6231
+ var AUDIT_RELEVANT_LINE_PATTERN = /\b(decision|decided|artifact|receipt|proof|commitment|committed|next action|follow[- ]?up|outcome|result|impact|roi|economics|token|cost|saved|open loop|gap|blocker|risk|owner|dri|writeback|rollback|quality score|mcp|tool call|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative|mcp__orgx__)\b/i;
6232
6232
  function parseAiSessionSources(value) {
6233
6233
  if (!value?.trim()) return [];
6234
6234
  const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
@@ -6889,6 +6889,769 @@ Rollback: ${plan.recommended_follow_up.rollback}`,
6889
6889
  );
6890
6890
  }
6891
6891
 
6892
+ // src/lib/work-graph.ts
6893
+ import { createHash as createHash4 } from "crypto";
6894
+ var WORK_GRAPH_SCHEMA_VERSION = "2026-05-07";
6895
+ var WORK_GRAPH_FINGERPRINT_VERSION = "wgf_v1";
6896
+ function clampScore2(value) {
6897
+ return Math.max(0, Math.min(100, Math.round(value)));
6898
+ }
6899
+ function hashJson(value) {
6900
+ return createHash4("sha256").update(JSON.stringify(value)).digest("hex");
6901
+ }
6902
+ function normalizeFingerprintText(value) {
6903
+ 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);
6904
+ }
6905
+ function shortHash(value, length = 16) {
6906
+ return hashJson(value).slice(0, length);
6907
+ }
6908
+ function sortedUnique(values) {
6909
+ return [...new Set(values)].sort();
6910
+ }
6911
+ function sourceClientForImport(source) {
6912
+ const raw = `${source.sourceId} ${source.sourceLabel}`.toLowerCase();
6913
+ if (raw.includes("codex")) return "codex";
6914
+ if (raw.includes("claude-code")) return "claude-code";
6915
+ if (raw.includes("claude")) return "claude";
6916
+ if (raw.includes("cursor")) return "cursor";
6917
+ if (raw.includes("openclaw")) return "openclaw";
6918
+ if (raw.includes("slack")) return "slack";
6919
+ if (raw.includes("github")) return "github";
6920
+ if (raw.includes("linear")) return "linear";
6921
+ if (raw.includes("mcp")) return "mcp";
6922
+ if (raw.includes("api")) return "api";
6923
+ if (raw.includes("manual") || raw.includes("wizard-audit-input")) return "manual";
6924
+ return "unknown";
6925
+ }
6926
+ function titleFromText(text2, fallback) {
6927
+ const normalized = text2.replace(/^\s*(decision|artifact|commitment|next action|follow[- ]?up|outcome|roi|economics|open loop|gap|blocker|risk|goal)\s*:\s*/i, "").trim();
6928
+ const firstSentence = normalized.split(/[.!?]\s/)[0]?.trim() || normalized;
6929
+ return (firstSentence || fallback).slice(0, 160);
6930
+ }
6931
+ function findingTypeForItem(item) {
6932
+ switch (item.type) {
6933
+ case "decision":
6934
+ return "decision";
6935
+ case "artifact":
6936
+ return "artifact";
6937
+ case "open_loop":
6938
+ return "blocker";
6939
+ case "commitment":
6940
+ case "next_action":
6941
+ case "outcome":
6942
+ return "action";
6943
+ case "economics":
6944
+ return "business";
6945
+ }
6946
+ }
6947
+ function evidenceRefFor(sourceId, index) {
6948
+ return `${sourceId}:derived:${index + 1}`;
6949
+ }
6950
+ function includesAny2(text2, patterns) {
6951
+ return patterns.some((pattern) => pattern.test(text2));
6952
+ }
6953
+ function buildCoverage(imports, connectedSources, missingSources) {
6954
+ const allText = imports.map((source) => source.text).join("\n").toLowerCase();
6955
+ const orgxObserved = /\borgx\b|useorgx|mcp__orgx__|orgx_/i.test(allText);
6956
+ const mcpObserved = /\bmcp\b|mcp__|tool call|tools\/call|call_tool|orgx_emit_activity/i.test(allText);
6957
+ const orgxMcpCalled = /mcp__orgx__|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative/i.test(allText);
6958
+ const skillOnlySignal = orgxObserved && !orgxMcpCalled && /\bskill|instructions|agent|workflow\b/i.test(allText);
6959
+ return {
6960
+ connected: [...connectedSources],
6961
+ missing: [...missingSources],
6962
+ mcpObserved,
6963
+ orgxObserved,
6964
+ orgxMcpCalled,
6965
+ skillOnlySignal
6966
+ };
6967
+ }
6968
+ function buildDerivedFindings(imports) {
6969
+ const findings = [];
6970
+ let derivedIndex = 0;
6971
+ for (const source of imports) {
6972
+ const sourceClient = sourceClientForImport(source);
6973
+ const lines = source.text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
6974
+ for (const line of lines) {
6975
+ const lower = line.toLowerCase();
6976
+ if (includesAny2(lower, [/\b(owner|dri|founder|teammate|customer|user|stakeholder|buyer|reviewer)\b/])) {
6977
+ findings.push({
6978
+ type: "person",
6979
+ title: titleFromText(line, "Person or stakeholder signal"),
6980
+ summary: line,
6981
+ source_client: sourceClient,
6982
+ source_id: source.sourceId,
6983
+ evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
6984
+ confidence: 0.62,
6985
+ metadata: { source_label: source.sourceLabel }
6986
+ });
6987
+ }
6988
+ if (includesAny2(lower, [/\b(product surface|surface|live room|command center|dashboard|widget|plugin|wizard|audit|onboarding|mcp|slack|github|linear)\b/])) {
6989
+ findings.push({
6990
+ type: "product_surface",
6991
+ title: titleFromText(line, "Product surface signal"),
6992
+ summary: line,
6993
+ source_client: sourceClient,
6994
+ source_id: source.sourceId,
6995
+ evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
6996
+ confidence: 0.66,
6997
+ metadata: { source_label: source.sourceLabel }
6998
+ });
6999
+ }
7000
+ if (includesAny2(lower, [/\b(goal|objective|initiative|workstream|milestone|roadmap|launch)\b/])) {
7001
+ findings.push({
7002
+ type: "goal",
7003
+ title: titleFromText(line, "Goal signal"),
7004
+ summary: line,
7005
+ source_client: sourceClient,
7006
+ source_id: source.sourceId,
7007
+ evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
7008
+ confidence: 0.64,
7009
+ metadata: { source_label: source.sourceLabel }
7010
+ });
7011
+ }
7012
+ }
7013
+ }
7014
+ return findings;
7015
+ }
7016
+ function buildWorkGraphEvents(imports) {
7017
+ return imports.map((source, index) => ({
7018
+ schema_version: WORK_GRAPH_SCHEMA_VERSION,
7019
+ source_client: sourceClientForImport(source),
7020
+ source_id: source.sourceId,
7021
+ source_label: source.sourceLabel,
7022
+ event_type: "session_excerpt",
7023
+ text: source.text,
7024
+ evidence_ref: `${source.sourceId}:excerpt`,
7025
+ metadata: {
7026
+ import_index: index,
7027
+ line_count: source.text.split(/\r?\n/).filter(Boolean).length,
7028
+ raw_transcript_sent: false
7029
+ }
7030
+ }));
7031
+ }
7032
+ function buildWorkGraphFindings(imports) {
7033
+ const loopFindings = extractFounderLoopItems(imports).map((item) => {
7034
+ const source = imports.find((candidate) => candidate.sourceId === item.sourceId);
7035
+ const sourceClient = source ? sourceClientForImport(source) : "unknown";
7036
+ const type = findingTypeForItem(item);
7037
+ return {
7038
+ type,
7039
+ title: titleFromText(item.text, type),
7040
+ summary: item.text,
7041
+ source_client: sourceClient,
7042
+ source_id: item.sourceId,
7043
+ evidence_ref: item.evidenceRef,
7044
+ confidence: type === "decision" || type === "artifact" ? 0.82 : 0.72,
7045
+ metadata: {
7046
+ source_label: item.sourceLabel,
7047
+ founder_loop_type: item.type,
7048
+ line_number: item.lineNumber
7049
+ }
7050
+ };
7051
+ });
7052
+ const seen = /* @__PURE__ */ new Set();
7053
+ return [...loopFindings, ...buildDerivedFindings(imports)].filter((finding) => {
7054
+ const key = `${finding.type}:${finding.source_id}:${finding.summary.toLowerCase()}`;
7055
+ if (seen.has(key)) return false;
7056
+ seen.add(key);
7057
+ return true;
7058
+ });
7059
+ }
7060
+ function buildMissedOpportunities(coverage, findings) {
7061
+ const opportunities = [];
7062
+ const hasActionableSignal = findings.some(
7063
+ (finding) => ["decision", "artifact", "blocker", "action"].includes(finding.type)
7064
+ );
7065
+ if (hasActionableSignal && !coverage.orgxMcpCalled) {
7066
+ opportunities.push({
7067
+ type: "missed_orchestration_opportunity",
7068
+ title: "High-value work happened without an OrgX MCP write",
7069
+ summary: "The source contains decisions, artifacts, blockers, or actions, but no durable OrgX MCP call was detected.",
7070
+ source_client: "wizard",
7071
+ source_id: "work-graph",
7072
+ evidence_ref: "work-graph:coverage:orgx-mcp",
7073
+ confidence: coverage.orgxObserved ? 0.78 : 0.7,
7074
+ metadata: {
7075
+ orgx_observed: coverage.orgxObserved,
7076
+ mcp_observed: coverage.mcpObserved,
7077
+ skill_only_signal: coverage.skillOnlySignal
7078
+ }
7079
+ });
7080
+ }
7081
+ if (coverage.missing.length > 0) {
7082
+ opportunities.push({
7083
+ type: "missed_orchestration_opportunity",
7084
+ title: "Source coverage is incomplete",
7085
+ summary: `Missing sources: ${coverage.missing.join(", ")}.`,
7086
+ source_client: "wizard",
7087
+ source_id: "work-graph",
7088
+ evidence_ref: "work-graph:coverage:missing",
7089
+ confidence: 0.76,
7090
+ metadata: { missing_sources: coverage.missing }
7091
+ });
7092
+ }
7093
+ return opportunities;
7094
+ }
7095
+ function scoreOpportunity(coverage, findings) {
7096
+ const count = (type) => findings.filter((finding) => finding.type === type).length;
7097
+ const decisions = count("decision");
7098
+ const artifacts = count("artifact");
7099
+ const blockers = count("blocker");
7100
+ const actions = count("action");
7101
+ const people = count("person");
7102
+ const business = count("business");
7103
+ const surfaces = count("product_surface");
7104
+ const goals = count("goal");
7105
+ const evidenceQuality = clampScore2(35 + Math.min(35, artifacts * 9 + decisions * 6) + Math.min(20, coverage.connected.length * 6) - coverage.missing.length * 8);
7106
+ const urgency = clampScore2(20 + blockers * 18 + actions * 7 + decisions * 5);
7107
+ const ownerClarity = clampScore2(25 + people * 18 + (findings.some((finding) => /\b(owner|dri|responsible|founder)\b/i.test(finding.summary)) ? 35 : 0));
7108
+ const automationPotential = clampScore2(30 + actions * 10 + surfaces * 7 + (coverage.mcpObserved ? 12 : 0));
7109
+ const valuePotential = clampScore2(25 + business * 20 + artifacts * 8 + goals * 10 + surfaces * 5);
7110
+ const orgxFit = clampScore2(35 + decisions * 8 + blockers * 10 + artifacts * 7 + (coverage.orgxObserved ? 10 : 0) + (!coverage.orgxMcpCalled ? 10 : 0));
7111
+ return {
7112
+ overall: clampScore2((valuePotential + evidenceQuality + urgency + ownerClarity + automationPotential + orgxFit) / 6),
7113
+ value_potential: valuePotential,
7114
+ evidence_quality: evidenceQuality,
7115
+ urgency,
7116
+ owner_clarity: ownerClarity,
7117
+ automation_potential: automationPotential,
7118
+ orgx_fit: orgxFit
7119
+ };
7120
+ }
7121
+ function inferFinalState(findings) {
7122
+ if (findings.some((finding) => finding.type === "blocker")) return "blocked";
7123
+ if (findings.some((finding) => finding.type === "artifact" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
7124
+ return "completed";
7125
+ }
7126
+ if (findings.some((finding) => finding.type === "action" || finding.type === "decision")) return "in_progress";
7127
+ return "unknown";
7128
+ }
7129
+ function buildKickoffs(findings, missed, score) {
7130
+ const blockers = findings.filter((finding) => finding.type === "blocker").slice(0, 3);
7131
+ const decisions = findings.filter((finding) => finding.type === "decision").slice(0, 3);
7132
+ const artifacts = findings.filter((finding) => finding.type === "artifact").slice(0, 3);
7133
+ const kickoffs = [];
7134
+ if (missed.length > 0) {
7135
+ kickoffs.push({
7136
+ title: "Install continuous OrgX writeback",
7137
+ summary: "Turn detected work into live OrgX activity, retro, outcome, and decision records.",
7138
+ reason: missed[0]?.summary ?? "A source produced work without durable OrgX orchestration.",
7139
+ finding_refs: missed.map((finding) => finding.evidence_ref).slice(0, 4),
7140
+ priority: score.overall >= 75 ? "p0" : "p1"
7141
+ });
7142
+ }
7143
+ if (blockers.length > 0 || decisions.length > 0) {
7144
+ kickoffs.push({
7145
+ title: "Resolve the hidden decision queue",
7146
+ summary: "Convert unresolved decisions and blockers into owner-visible OrgX decision cards.",
7147
+ reason: "The Work Graph found decisions or blockers that can slow execution if they stay buried in transcripts.",
7148
+ finding_refs: [...blockers, ...decisions].map((finding) => finding.evidence_ref).slice(0, 5),
7149
+ priority: blockers.length > 0 ? "p0" : "p1"
7150
+ });
7151
+ }
7152
+ if (artifacts.length > 0) {
7153
+ kickoffs.push({
7154
+ title: "Attach proof to the operating loop",
7155
+ summary: "Promote completed work into artifacts with owners, evidence refs, and next actions.",
7156
+ reason: "Completed work was detected; OrgX can make it queryable and reusable.",
7157
+ finding_refs: artifacts.map((finding) => finding.evidence_ref),
7158
+ priority: "p1"
7159
+ });
7160
+ }
7161
+ if (kickoffs.length === 0) {
7162
+ kickoffs.push({
7163
+ title: "Connect first work source",
7164
+ summary: "Add AI client, Slack, or MCP source coverage so OrgX can build a useful Work Graph.",
7165
+ reason: "The scan did not find enough structured work to recommend a specific operating initiative.",
7166
+ finding_refs: [],
7167
+ priority: "p2"
7168
+ });
7169
+ }
7170
+ return kickoffs.slice(0, 3);
7171
+ }
7172
+ function countFindingsByType(findings) {
7173
+ const counts = {};
7174
+ for (const finding of findings) {
7175
+ counts[finding.type] = (counts[finding.type] ?? 0) + 1;
7176
+ }
7177
+ return Object.fromEntries(
7178
+ Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
7179
+ );
7180
+ }
7181
+ function buildWorkGraphFingerprint(input) {
7182
+ const sourceClients = sortedUnique(input.findings.map((finding) => finding.source_client));
7183
+ const patternHashes = input.findings.map(
7184
+ (finding) => shortHash({
7185
+ type: finding.type,
7186
+ title: normalizeFingerprintText(finding.title),
7187
+ summary: normalizeFingerprintText(finding.summary),
7188
+ source_client: finding.source_client
7189
+ })
7190
+ ).sort();
7191
+ const kickoffHashes = input.kickoffs.map(
7192
+ (kickoff) => shortHash({
7193
+ title: normalizeFingerprintText(kickoff.title),
7194
+ summary: normalizeFingerprintText(kickoff.summary),
7195
+ priority: kickoff.priority
7196
+ })
7197
+ ).sort();
7198
+ const basis = {
7199
+ schema_version: WORK_GRAPH_SCHEMA_VERSION,
7200
+ fingerprint_version: WORK_GRAPH_FINGERPRINT_VERSION,
7201
+ workspace_hash: shortHash({
7202
+ id: normalizeFingerprintText(input.workspace.id),
7203
+ name: normalizeFingerprintText(input.workspace.name)
7204
+ }),
7205
+ source_clients: sourceClients,
7206
+ connected_source_hashes: sortedUnique(
7207
+ input.connectedSources.map((source) => shortHash(normalizeFingerprintText(source)))
7208
+ ),
7209
+ missing_source_hashes: sortedUnique(
7210
+ input.missingSources.map((source) => shortHash(normalizeFingerprintText(source)))
7211
+ ),
7212
+ finding_type_counts: countFindingsByType(input.findings),
7213
+ pattern_hashes: patternHashes,
7214
+ kickoff_hashes: kickoffHashes,
7215
+ raw_transcripts_included: false
7216
+ };
7217
+ const fingerprint = `wgf_${hashJson(basis).slice(0, 24)}`;
7218
+ return {
7219
+ fingerprint,
7220
+ basis,
7221
+ hydration: {
7222
+ strategy: "work_graph_fingerprint_claim",
7223
+ hydration_key: `orgx:work-graph:${fingerprint}`,
7224
+ eligible: input.findings.length > 0,
7225
+ notes: [
7226
+ "Use this fingerprint to claim the pre-signup Work Graph after account creation.",
7227
+ "The fingerprint is derived from normalized patterns, source coverage, and kickoff shapes, not raw transcripts."
7228
+ ]
7229
+ }
7230
+ };
7231
+ }
7232
+ function buildSessionReconciliationReport(input) {
7233
+ if (input.imports.length === 0) {
7234
+ throw new Error("At least one source import is required to build a Work Graph report.");
7235
+ }
7236
+ const generatedAt = input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
7237
+ const connectedSources = input.connectedSources ?? input.imports.map((source) => source.sourceLabel);
7238
+ const missingSources = input.missingSources ?? [];
7239
+ const coverage = buildCoverage(input.imports, connectedSources, missingSources);
7240
+ const events = buildWorkGraphEvents(input.imports);
7241
+ const findings = buildWorkGraphFindings(input.imports);
7242
+ const missed = buildMissedOpportunities(coverage, findings);
7243
+ const allFindings = [...findings, ...missed];
7244
+ const opportunityScore = scoreOpportunity(coverage, allFindings);
7245
+ const initiativeKickoffs = buildKickoffs(allFindings, missed, opportunityScore);
7246
+ const fingerprint = buildWorkGraphFingerprint({
7247
+ connectedSources,
7248
+ findings: allFindings,
7249
+ kickoffs: initiativeKickoffs,
7250
+ missingSources,
7251
+ workspace: input.workspace
7252
+ });
7253
+ const reportSeed = {
7254
+ generatedAt,
7255
+ imports: input.imports.map((source) => ({
7256
+ sourceId: source.sourceId,
7257
+ textHash: hashJson(source.text)
7258
+ })),
7259
+ workspace: input.workspace
7260
+ };
7261
+ const reportHash = hashJson(reportSeed);
7262
+ const sessionId = input.sessionId ?? `work-graph-${reportHash.slice(0, 16)}`;
7263
+ return {
7264
+ schema_version: WORK_GRAPH_SCHEMA_VERSION,
7265
+ report_id: reportHash.slice(0, 24),
7266
+ idempotency_key: `work-graph:${sessionId}:${reportHash.slice(0, 16)}`,
7267
+ work_graph_fingerprint: fingerprint.fingerprint,
7268
+ fingerprint_version: WORK_GRAPH_FINGERPRINT_VERSION,
7269
+ fingerprint_basis: fingerprint.basis,
7270
+ signup_hydration: fingerprint.hydration,
7271
+ generated_at: generatedAt,
7272
+ source_client: "wizard",
7273
+ session_id: sessionId,
7274
+ workspace: input.workspace,
7275
+ source_coverage: coverage,
7276
+ final_state: inferFinalState(allFindings),
7277
+ events,
7278
+ findings: allFindings,
7279
+ missed_orchestration_opportunities: missed,
7280
+ opportunity_score: opportunityScore,
7281
+ initiative_kickoffs: initiativeKickoffs,
7282
+ redaction_level: "summary_only",
7283
+ raw_transcripts_sent: false
7284
+ };
7285
+ }
7286
+ function renderWorkGraphMarkdown(report) {
7287
+ const lines = [];
7288
+ lines.push("# OrgX Work Graph Reconciliation");
7289
+ lines.push("");
7290
+ lines.push(`Generated: ${report.generated_at}`);
7291
+ lines.push(`Workspace: ${report.workspace.name} (${report.workspace.id})`);
7292
+ lines.push(`Report ID: ${report.report_id}`);
7293
+ lines.push(`Work graph fingerprint: ${report.work_graph_fingerprint}`);
7294
+ lines.push(`Hydration key: ${report.signup_hydration.hydration_key}`);
7295
+ lines.push(`Final state: ${report.final_state}`);
7296
+ lines.push("");
7297
+ lines.push("## Opportunity Score");
7298
+ lines.push("");
7299
+ lines.push(`Overall: ${report.opportunity_score.overall}/100`);
7300
+ lines.push(`Value potential: ${report.opportunity_score.value_potential}/100`);
7301
+ lines.push(`Evidence quality: ${report.opportunity_score.evidence_quality}/100`);
7302
+ lines.push(`Urgency: ${report.opportunity_score.urgency}/100`);
7303
+ lines.push(`Owner clarity: ${report.opportunity_score.owner_clarity}/100`);
7304
+ lines.push(`Automation potential: ${report.opportunity_score.automation_potential}/100`);
7305
+ lines.push(`OrgX fit: ${report.opportunity_score.orgx_fit}/100`);
7306
+ lines.push("");
7307
+ lines.push("## Source Coverage");
7308
+ lines.push("");
7309
+ lines.push(`Connected: ${report.source_coverage.connected.join(", ") || "none"}`);
7310
+ lines.push(`Missing: ${report.source_coverage.missing.join(", ") || "none"}`);
7311
+ lines.push(`MCP observed: ${report.source_coverage.mcpObserved ? "yes" : "no"}`);
7312
+ lines.push(`OrgX observed: ${report.source_coverage.orgxObserved ? "yes" : "no"}`);
7313
+ lines.push(`OrgX MCP called: ${report.source_coverage.orgxMcpCalled ? "yes" : "no"}`);
7314
+ lines.push("");
7315
+ lines.push("## Top Findings");
7316
+ lines.push("");
7317
+ for (const finding of report.findings.slice(0, 12)) {
7318
+ lines.push(`- [${finding.type}] ${finding.title} (${finding.evidence_ref})`);
7319
+ }
7320
+ lines.push("");
7321
+ lines.push("## Missed Orchestration");
7322
+ lines.push("");
7323
+ if (report.missed_orchestration_opportunities.length === 0) {
7324
+ lines.push("- No missed orchestration opportunities detected.");
7325
+ } else {
7326
+ for (const finding of report.missed_orchestration_opportunities) {
7327
+ lines.push(`- ${finding.title}: ${finding.summary}`);
7328
+ }
7329
+ }
7330
+ lines.push("");
7331
+ lines.push("## Initiative Kickoffs");
7332
+ lines.push("");
7333
+ for (const kickoff of report.initiative_kickoffs) {
7334
+ lines.push(`- [${kickoff.priority}] ${kickoff.title}: ${kickoff.summary}`);
7335
+ }
7336
+ lines.push("");
7337
+ lines.push("## Signup Hydration");
7338
+ lines.push("");
7339
+ lines.push(`- Strategy: ${report.signup_hydration.strategy}`);
7340
+ lines.push(`- Eligible: ${report.signup_hydration.eligible ? "yes" : "no"}`);
7341
+ for (const note of report.signup_hydration.notes) {
7342
+ lines.push(`- ${note}`);
7343
+ }
7344
+ lines.push("");
7345
+ lines.push("## Privacy");
7346
+ lines.push("");
7347
+ lines.push("- Raw transcripts were not sent.");
7348
+ lines.push("- This report contains summaries, hashes, and evidence refs only.");
7349
+ return lines.join("\n");
7350
+ }
7351
+
7352
+ // src/lib/runtime-hooks.ts
7353
+ import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
7354
+ import { homedir as homedir2 } from "os";
7355
+ import { dirname as dirname4, join as join5 } from "path";
7356
+ var HOOK_MARKER = "orgx-session-hook.mjs";
7357
+ var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
7358
+ var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
7359
+ function defaultPaths(options = {}) {
7360
+ const hookDir = join5(ORGX_WIZARD_CONFIG_HOME, "hooks");
7361
+ return {
7362
+ claudeSettingsPath: options.claudeSettingsPath ?? join5(CLAUDE_DIR, "settings.json"),
7363
+ codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join5(CODEX_DIR, "config.toml"),
7364
+ codexHooksPath: options.codexHooksPath ?? join5(CODEX_DIR, "hooks.json"),
7365
+ hookScriptPath: options.hookScriptPath ?? join5(hookDir, HOOK_MARKER),
7366
+ outboxPath: options.outboxPath ?? join5(hookDir, "events.jsonl")
7367
+ };
7368
+ }
7369
+ function countJsonlLines(path) {
7370
+ const raw = readTextIfExists(path);
7371
+ if (!raw) return 0;
7372
+ return raw.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
7373
+ }
7374
+ function backupPath(path, now) {
7375
+ const timestamp = now.toISOString().replace(/[:.]/g, "-");
7376
+ return `${path}.bak.${timestamp}`;
7377
+ }
7378
+ function backupExisting(path, now) {
7379
+ if (!existsSync6(path)) return null;
7380
+ const backup = backupPath(path, now);
7381
+ copyFileSync(path, backup);
7382
+ return backup;
7383
+ }
7384
+ function hasOrgxHook(raw) {
7385
+ return Boolean(raw?.includes(HOOK_MARKER));
7386
+ }
7387
+ function codexHooksEnabled(raw) {
7388
+ return Boolean(raw && /^\s*codex_hooks\s*=\s*true\s*$/m.test(raw));
7389
+ }
7390
+ function codexHasNotify(raw) {
7391
+ return Boolean(raw && /^\s*notify\s*=/m.test(raw));
7392
+ }
7393
+ function buildRuntimeHookScriptContent() {
7394
+ return `#!/usr/bin/env node
7395
+ import { appendFileSync, mkdirSync } from "node:fs";
7396
+ import { dirname, join } from "node:path";
7397
+ import { homedir } from "node:os";
7398
+
7399
+ function parseArgs(argv) {
7400
+ const args = {};
7401
+ for (const arg of argv) {
7402
+ if (!arg.startsWith("--")) continue;
7403
+ const [key, ...rest] = arg.slice(2).split("=");
7404
+ args[key] = rest.length > 0 ? rest.join("=") : "true";
7405
+ }
7406
+ return args;
7407
+ }
7408
+
7409
+ function pickString(...values) {
7410
+ for (const value of values) {
7411
+ if (typeof value !== "string") continue;
7412
+ const trimmed = value.trim();
7413
+ if (trimmed) return trimmed;
7414
+ }
7415
+ return undefined;
7416
+ }
7417
+
7418
+ async function readStdin() {
7419
+ const chunks = [];
7420
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
7421
+ return Buffer.concat(chunks).toString("utf8");
7422
+ }
7423
+
7424
+ function parseJson(value) {
7425
+ try {
7426
+ const parsed = JSON.parse(value || "{}");
7427
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
7428
+ } catch {
7429
+ return {};
7430
+ }
7431
+ }
7432
+
7433
+ function summarize(payload) {
7434
+ const toolName = pickString(payload.tool_name, payload.toolName, payload.tool?.name, payload.name);
7435
+ const prompt = pickString(payload.prompt);
7436
+ return {
7437
+ tool_name: toolName,
7438
+ prompt_chars: prompt ? prompt.length : undefined,
7439
+ payload_keys: Object.keys(payload).slice(0, 40),
7440
+ };
7441
+ }
7442
+
7443
+ const args = parseArgs(process.argv.slice(2));
7444
+ const raw = await readStdin();
7445
+ const payload = parseJson(raw);
7446
+ const outbox = pickString(
7447
+ process.env.ORGX_WIZARD_HOOK_OUTBOX,
7448
+ args.outbox,
7449
+ join(homedir(), ".config", "useorgx", "wizard", "hooks", "events.jsonl")
7450
+ );
7451
+ const event = pickString(args.event, payload.hook_event_name, payload.hookEventName, payload.event, payload.eventName, "unknown");
7452
+ const sourceClient = pickString(args.source_client, args["source-client"], "unknown");
7453
+
7454
+ const record = {
7455
+ schema_version: "2026-05-07",
7456
+ source: "orgx_wizard_runtime_hook",
7457
+ source_client: sourceClient,
7458
+ event,
7459
+ session_id: pickString(payload.session_id, payload.sessionId, payload.conversation_id, payload.conversationId),
7460
+ turn_id: pickString(payload.turn_id, payload.turnId),
7461
+ cwd: pickString(payload.cwd, payload.working_directory, payload.workspace, process.cwd()),
7462
+ transcript_path: pickString(payload.transcript_path, payload.transcriptPath),
7463
+ timestamp: new Date().toISOString(),
7464
+ summary: summarize(payload),
7465
+ };
7466
+
7467
+ try {
7468
+ mkdirSync(dirname(outbox), { recursive: true, mode: 0o700 });
7469
+ appendFileSync(outbox, JSON.stringify(record) + "\\n", { encoding: "utf8", mode: 0o600 });
7470
+ } catch {
7471
+ // Hooks must never break the user's agent runtime.
7472
+ }
7473
+
7474
+ process.exit(0);
7475
+ `;
7476
+ }
7477
+ function buildHookCommand(params) {
7478
+ return [
7479
+ "node",
7480
+ JSON.stringify(params.hookScriptPath),
7481
+ `--event=${params.event}`,
7482
+ `--source_client=${params.sourceClient}`,
7483
+ `--outbox=${params.outboxPath}`
7484
+ ].join(" ");
7485
+ }
7486
+ function mergeCodexHooks(raw, paths) {
7487
+ const value = parseJsonObject(raw);
7488
+ const hooks = isRecord(value.hooks) ? value.hooks : {};
7489
+ let changed = false;
7490
+ for (const event of HOOK_EVENTS) {
7491
+ const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
7492
+ const command = buildHookCommand({
7493
+ event,
7494
+ hookScriptPath: paths.hookScriptPath,
7495
+ outboxPath: paths.outboxPath,
7496
+ sourceClient: "codex"
7497
+ });
7498
+ const already = existing.some(
7499
+ (entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
7500
+ );
7501
+ if (!already) {
7502
+ hooks[event] = [...existing, { command }];
7503
+ changed = true;
7504
+ }
7505
+ }
7506
+ value.hooks = hooks;
7507
+ return { changed: changed || !raw, value };
7508
+ }
7509
+ function mergeClaudeHooks(raw, paths) {
7510
+ const value = parseJsonObject(raw);
7511
+ const hooksRoot = isRecord(value.hooks) ? value.hooks : {};
7512
+ let changed = false;
7513
+ for (const event of CLAUDE_HOOK_EVENTS) {
7514
+ const list = Array.isArray(hooksRoot[event]) ? hooksRoot[event] : [];
7515
+ const matcher = event === "PreToolUse" || event === "PostToolUse" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
7516
+ const command = buildHookCommand({
7517
+ event,
7518
+ hookScriptPath: paths.hookScriptPath,
7519
+ outboxPath: paths.outboxPath,
7520
+ sourceClient: "claude-code"
7521
+ });
7522
+ let rule = list.find((entry) => isRecord(entry) && entry.matcher === matcher);
7523
+ if (!rule) {
7524
+ rule = { matcher, hooks: [] };
7525
+ list.push(rule);
7526
+ changed = true;
7527
+ }
7528
+ const hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
7529
+ const already = hooks.some(
7530
+ (entry) => isRecord(entry) && entry.type === "command" && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
7531
+ );
7532
+ if (!already) {
7533
+ hooks.push({ type: "command", command });
7534
+ rule.hooks = hooks;
7535
+ changed = true;
7536
+ }
7537
+ hooksRoot[event] = list;
7538
+ }
7539
+ value.hooks = hooksRoot;
7540
+ return { changed: changed || !raw, value };
7541
+ }
7542
+ function ensureCodexHooksFeature(raw) {
7543
+ const current = raw ?? "";
7544
+ if (codexHooksEnabled(current)) {
7545
+ return { changed: false, value: current };
7546
+ }
7547
+ if (/^\s*codex_hooks\s*=\s*false\s*$/m.test(current)) {
7548
+ return {
7549
+ changed: true,
7550
+ value: current.replace(/^\s*codex_hooks\s*=\s*false\s*$/m, "codex_hooks = true")
7551
+ };
7552
+ }
7553
+ if (/^\s*\[features\]\s*$/m.test(current)) {
7554
+ return {
7555
+ changed: true,
7556
+ value: current.replace(/^(\s*\[features\]\s*)$/m, "$1\ncodex_hooks = true")
7557
+ };
7558
+ }
7559
+ const suffix = current.trimEnd().length > 0 ? "\n\n" : "";
7560
+ return {
7561
+ changed: true,
7562
+ value: `${current.trimEnd()}${suffix}[features]
7563
+ codex_hooks = true
7564
+ `
7565
+ };
7566
+ }
7567
+ function inspectRuntimeHooks(options = {}) {
7568
+ const paths = defaultPaths(options);
7569
+ const codexConfigRaw = readTextIfExists(paths.codexConfigPath);
7570
+ const codexHooksRaw = readTextIfExists(paths.codexHooksPath);
7571
+ const claudeSettingsRaw = readTextIfExists(paths.claudeSettingsPath);
7572
+ return {
7573
+ paths,
7574
+ installed: {
7575
+ claudeCode: hasOrgxHook(claudeSettingsRaw),
7576
+ codex: hasOrgxHook(codexHooksRaw),
7577
+ hookScript: existsSync6(paths.hookScriptPath)
7578
+ },
7579
+ codex: {
7580
+ configExists: Boolean(codexConfigRaw),
7581
+ hooksEnabled: codexHooksEnabled(codexConfigRaw),
7582
+ hasNotify: codexHasNotify(codexConfigRaw),
7583
+ notifyPreserved: !codexHasNotify(codexConfigRaw) || !hasOrgxHook(codexConfigRaw)
7584
+ },
7585
+ outboxEvents: countJsonlLines(paths.outboxPath)
7586
+ };
7587
+ }
7588
+ function installRuntimeHooks(targets, options = {}) {
7589
+ const now = options.now ?? /* @__PURE__ */ new Date();
7590
+ const paths = defaultPaths(options);
7591
+ const backups = [];
7592
+ const changed = {
7593
+ claudeCode: false,
7594
+ codex: false,
7595
+ codexConfig: false,
7596
+ hookScript: false
7597
+ };
7598
+ mkdirSync3(dirname4(paths.hookScriptPath), { recursive: true, mode: 448 });
7599
+ const scriptContent = buildRuntimeHookScriptContent();
7600
+ if (readTextIfExists(paths.hookScriptPath) !== scriptContent) {
7601
+ const backup = backupExisting(paths.hookScriptPath, now);
7602
+ if (backup) backups.push(backup);
7603
+ writeTextFile(paths.hookScriptPath, scriptContent, { mode: 448 });
7604
+ changed.hookScript = true;
7605
+ }
7606
+ if (targets.includes("codex")) {
7607
+ const rawConfig = readTextIfExists(paths.codexConfigPath);
7608
+ const nextConfig = ensureCodexHooksFeature(rawConfig);
7609
+ if (nextConfig.changed) {
7610
+ const backup = backupExisting(paths.codexConfigPath, now);
7611
+ if (backup) backups.push(backup);
7612
+ writeTextFile(paths.codexConfigPath, nextConfig.value, { mode: 384 });
7613
+ changed.codexConfig = true;
7614
+ }
7615
+ const rawHooks = readTextIfExists(paths.codexHooksPath);
7616
+ const nextHooks = mergeCodexHooks(rawHooks, paths);
7617
+ if (nextHooks.changed) {
7618
+ const backup = backupExisting(paths.codexHooksPath, now);
7619
+ if (backup) backups.push(backup);
7620
+ writeJsonFile(paths.codexHooksPath, nextHooks.value, { mode: 384 });
7621
+ changed.codex = true;
7622
+ }
7623
+ }
7624
+ if (targets.includes("claude-code")) {
7625
+ const rawSettings = readTextIfExists(paths.claudeSettingsPath);
7626
+ const nextSettings = mergeClaudeHooks(rawSettings, paths);
7627
+ if (nextSettings.changed) {
7628
+ const backup = backupExisting(paths.claudeSettingsPath, now);
7629
+ if (backup) backups.push(backup);
7630
+ writeJsonFile(paths.claudeSettingsPath, nextSettings.value, { mode: 384 });
7631
+ changed.claudeCode = true;
7632
+ }
7633
+ }
7634
+ return {
7635
+ ...inspectRuntimeHooks(options),
7636
+ changed,
7637
+ backups
7638
+ };
7639
+ }
7640
+ function parseRuntimeHookTargets(value) {
7641
+ if (!value?.trim()) return ["codex", "claude-code"];
7642
+ const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
7643
+ const expanded = requested.includes("all") ? ["codex", "claude-code"] : requested;
7644
+ const normalized = expanded.map((target) => {
7645
+ if (target === "claude" || target === "claude_code") return "claude-code";
7646
+ return target;
7647
+ });
7648
+ const invalid = normalized.filter((target) => target !== "codex" && target !== "claude-code");
7649
+ if (invalid.length > 0) {
7650
+ throw new Error(`Unsupported hook target: ${invalid.join(", ")}. Use codex, claude-code, or all.`);
7651
+ }
7652
+ return [...new Set(normalized)];
7653
+ }
7654
+
6892
7655
  // src/spinner.ts
6893
7656
  import ora from "ora";
6894
7657
  import pc2 from "picocolors";
@@ -6992,12 +7755,31 @@ function printPluginMutationReport(report) {
6992
7755
  function formatScoreLine(scores) {
6993
7756
  return Object.entries(scores).map(([dimension, score]) => `${dimension}=${score}`).join(" ");
6994
7757
  }
7758
+ function formatWorkGraphScoreLine(score) {
7759
+ return [
7760
+ `overall=${score.overall}`,
7761
+ `value=${score.value_potential}`,
7762
+ `evidence=${score.evidence_quality}`,
7763
+ `urgency=${score.urgency}`,
7764
+ `owner=${score.owner_clarity}`,
7765
+ `automation=${score.automation_potential}`,
7766
+ `orgx_fit=${score.orgx_fit}`
7767
+ ].join(" ");
7768
+ }
7769
+ function printRuntimeHookInspection(report) {
7770
+ console.log(` ${report.installed.hookScript ? ICON.ok : ICON.warn} ${pc3.bold("hook script ")} ${report.installed.hookScript ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.hookScriptPath)}`);
7771
+ console.log(` ${report.installed.codex ? ICON.ok : ICON.warn} ${pc3.bold("Codex ")} ${report.installed.codex ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.codexHooksPath)}`);
7772
+ console.log(` ${report.codex.hooksEnabled ? ICON.ok : ICON.warn} ${pc3.bold("Codex flag ")} ${report.codex.hooksEnabled ? pc3.green("enabled") : pc3.yellow("not enabled")} ${pc3.dim(report.paths.codexConfigPath)}`);
7773
+ console.log(` ${report.codex.notifyPreserved ? ICON.ok : ICON.warn} ${pc3.bold("notify ")} ${report.codex.notifyPreserved ? pc3.green("preserved") : pc3.yellow("check config")} ${pc3.dim(report.codex.hasNotify ? "existing notify detected" : "no notify entry")}`);
7774
+ console.log(` ${report.installed.claudeCode ? ICON.ok : ICON.warn} ${pc3.bold("Claude Code ")} ${report.installed.claudeCode ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.claudeSettingsPath)}`);
7775
+ console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
7776
+ }
6995
7777
  function readAuditInput(options, interactive) {
6996
7778
  if (options.input?.trim()) {
6997
- return readFileSync4(resolve(options.input.trim()), "utf8");
7779
+ return readFileSync5(resolve(options.input.trim()), "utf8");
6998
7780
  }
6999
7781
  if (!process.stdin.isTTY) {
7000
- return readFileSync4(0, "utf8");
7782
+ return readFileSync5(0, "utf8");
7001
7783
  }
7002
7784
  if (!interactive) {
7003
7785
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -7171,6 +7953,60 @@ async function runAuditCommand(options) {
7171
7953
  console.log(` ${ICON.ok} ${pc3.green("follow-up ")} ${pc3.bold(followUp.title)} ${pc3.dim(followUp.id)}`);
7172
7954
  }
7173
7955
  }
7956
+ async function runWorkGraphCommand(options, defaults = {}) {
7957
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
7958
+ const commandOptions = {
7959
+ ...options,
7960
+ ...options.from?.trim() ? {} : defaults.from ? { from: defaults.from } : {}
7961
+ };
7962
+ const auditImports = await readAuditImports(commandOptions, interactive);
7963
+ const workspace = await resolveAuditWorkspace(commandOptions);
7964
+ const report = buildSessionReconciliationReport({
7965
+ connectedSources: [
7966
+ ...auditImports.connectedSources,
7967
+ ...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
7968
+ ],
7969
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7970
+ imports: auditImports.imports,
7971
+ missingSources: [
7972
+ ...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
7973
+ ...auditImports.missingSources
7974
+ ],
7975
+ workspace
7976
+ });
7977
+ const markdown = renderWorkGraphMarkdown(report);
7978
+ const outputDir = resolve(commandOptions.outputDir?.trim() || ".orgx/work-graph");
7979
+ const timestamp = report.generated_at.replace(/[:.]/g, "-");
7980
+ const jsonPath = resolve(outputDir, `work-graph-report-${timestamp}.json`);
7981
+ const markdownPath = resolve(outputDir, `work-graph-report-${timestamp}.md`);
7982
+ writeJsonFile(jsonPath, report);
7983
+ writeTextFile(markdownPath, markdown);
7984
+ if (commandOptions.json) {
7985
+ console.log(JSON.stringify({
7986
+ jsonPath,
7987
+ markdownPath,
7988
+ reportId: report.report_id,
7989
+ workGraphFingerprint: report.work_graph_fingerprint,
7990
+ hydrationKey: report.signup_hydration.hydration_key,
7991
+ finalState: report.final_state,
7992
+ opportunityScore: report.opportunity_score,
7993
+ missedOrchestration: report.missed_orchestration_opportunities.length,
7994
+ kickoffCount: report.initiative_kickoffs.length
7995
+ }, null, 2));
7996
+ return;
7997
+ }
7998
+ console.log(` ${ICON.ok} ${pc3.green("work graph ")} ${pc3.dim(markdownPath)}`);
7999
+ console.log(` ${ICON.ok} ${pc3.green("report id ")} ${pc3.dim(report.report_id)}`);
8000
+ console.log(` ${ICON.ok} ${pc3.green("fingerprint ")} ${pc3.dim(report.work_graph_fingerprint)}`);
8001
+ console.log(` ${ICON.ok} ${pc3.green("state ")} ${pc3.dim(report.final_state)}`);
8002
+ console.log(` ${ICON.ok} ${pc3.green("score ")} ${pc3.dim(formatWorkGraphScoreLine(report.opportunity_score))}`);
8003
+ const missed = report.missed_orchestration_opportunities.length;
8004
+ const missedColor = missed > 0 ? pc3.yellow : pc3.green;
8005
+ console.log(` ${missed > 0 ? ICON.warn : ICON.ok} ${missedColor("missed ")} ${pc3.dim(`${missed} orchestration opportunit${missed === 1 ? "y" : "ies"}`)}`);
8006
+ for (const kickoff of report.initiative_kickoffs) {
8007
+ console.log(` ${ICON.skip} ${pc3.bold(kickoff.priority.padEnd(3))} ${kickoff.title}`);
8008
+ }
8009
+ }
7174
8010
  async function checkPluginStatusesCompact() {
7175
8011
  const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
7176
8012
  spinner.start();
@@ -8129,7 +8965,7 @@ function printDoctorReport(report, assessment) {
8129
8965
  async function main() {
8130
8966
  const program = new Command();
8131
8967
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
8132
- const pkgVersion = true ? "0.1.26" : void 0;
8968
+ const pkgVersion = true ? "0.1.27" : void 0;
8133
8969
  program.version(pkgVersion ?? "unknown", "-V, --version");
8134
8970
  program.hook("preAction", () => {
8135
8971
  console.log(renderBanner(pkgVersion));
@@ -8828,6 +9664,59 @@ async function main() {
8828
9664
  });
8829
9665
  await runAuditCommand(options);
8830
9666
  });
9667
+ const workGraph = program.command("work-graph").description("Build a redacted OrgX Work Graph report from AI-client, Slack, MCP, or manual context.");
9668
+ workGraph.command("preview").description("Preview the live Work Graph opportunity map without writing to OrgX.").option("--input <path>", "source transcript or summary file; stdin is used when piped").option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all").option("--session-limit <count>", "max recent sessions to import per selected source", "3").option("--session-days <days>", "lookback window for local AI-session imports", "30").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--source-label <label>", "label for the imported manual source").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only previews").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
9669
+ await safeTrackWizardTelemetry("work_graph_preview_started", {
9670
+ command: "work-graph preview",
9671
+ from: options.from ?? "manual"
9672
+ });
9673
+ await runWorkGraphCommand(options);
9674
+ });
9675
+ const sessions = program.command("sessions").description("Inspect and reconcile local AI sessions into OrgX-ready Work Graph reports.");
9676
+ sessions.command("reconcile").description("Backfill recent Codex and Claude sessions into a redacted Work Graph report.").option("--from <sources>", "auto-import recent local AI sessions: codex, claude, or all", "all").option("--session-limit <count>", "max recent sessions to import per selected source", "5").option("--session-days <days>", "lookback window for local AI-session imports", "7").option("--codex-sessions-dir <path>", "override Codex sessions directory for import").option("--claude-projects-dir <path>", "override Claude projects directory for import").option("--workspace-id <id>", "workspace id override; defaults to the current OrgX workspace when authenticated").option("--workspace-name <name>", "workspace name override for local-only reconciliation").option("--output-dir <path>", "directory for generated Work Graph JSON and Markdown", ".orgx/work-graph").option("--json", "emit a JSON command summary").action(async (options) => {
9677
+ await safeTrackWizardTelemetry("sessions_reconcile_started", {
9678
+ command: "sessions reconcile",
9679
+ from: options.from ?? "all"
9680
+ });
9681
+ await runWorkGraphCommand(options, { from: "all" });
9682
+ });
9683
+ const hooks = program.command("hooks").description("Inspect or install passive OrgX runtime hooks for local agent clients.");
9684
+ hooks.command("doctor").description("Show Codex and Claude Code runtime hook wiring status.").option("--json", "emit a JSON command summary").action(async (options) => {
9685
+ const report = inspectRuntimeHooks();
9686
+ await safeTrackWizardTelemetry("hooks_doctor_ran", {
9687
+ command: "hooks doctor",
9688
+ codex_installed: report.installed.codex,
9689
+ claude_code_installed: report.installed.claudeCode,
9690
+ hook_script_installed: report.installed.hookScript
9691
+ });
9692
+ if (options.json) {
9693
+ console.log(JSON.stringify(report, null, 2));
9694
+ return;
9695
+ }
9696
+ printRuntimeHookInspection(report);
9697
+ });
9698
+ hooks.command("install").description("Install passive OrgX runtime hooks for Codex and/or Claude Code.").option("--targets <targets>", "comma-separated targets: codex, claude-code, or all", "all").option("--json", "emit a JSON command summary").action(async (options) => {
9699
+ const targets = parseRuntimeHookTargets(options.targets);
9700
+ const result = installRuntimeHooks(targets);
9701
+ await safeTrackWizardTelemetry("hooks_install_ran", {
9702
+ command: "hooks install",
9703
+ targets: targets.join(","),
9704
+ codex_changed: result.changed.codex,
9705
+ claude_code_changed: result.changed.claudeCode
9706
+ });
9707
+ if (options.json) {
9708
+ console.log(JSON.stringify(result, null, 2));
9709
+ return;
9710
+ }
9711
+ printRuntimeHookInspection(result);
9712
+ if (result.backups.length > 0) {
9713
+ console.log("");
9714
+ console.log(pc3.dim(" backups"));
9715
+ for (const backup of result.backups) {
9716
+ console.log(` ${ICON.skip} ${pc3.dim(backup)}`);
9717
+ }
9718
+ }
9719
+ });
8831
9720
  program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
8832
9721
  const spinner = createOrgxSpinner("Running OrgX health check");
8833
9722
  spinner.start();