@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 +894 -5
- package/dist/cli.js.map +1 -1
- package/package.json +12 -11
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
|
|
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
|
|
7779
|
+
return readFileSync5(resolve(options.input.trim()), "utf8");
|
|
6998
7780
|
}
|
|
6999
7781
|
if (!process.stdin.isTTY) {
|
|
7000
|
-
return
|
|
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.
|
|
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();
|