@useorgx/wizard 0.1.26 → 0.1.28
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 +1480 -5
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
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,1338 @@ 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 slugPart(value) {
|
|
6912
|
+
const normalized = normalizeFingerprintText(value).replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 64);
|
|
6913
|
+
return normalized || "unknown";
|
|
6914
|
+
}
|
|
6915
|
+
function sourceClientForImport(source) {
|
|
6916
|
+
const raw = `${source.sourceId} ${source.sourceLabel}`.toLowerCase();
|
|
6917
|
+
if (raw.includes("codex")) return "codex";
|
|
6918
|
+
if (raw.includes("claude-code")) return "claude-code";
|
|
6919
|
+
if (raw.includes("claude")) return "claude";
|
|
6920
|
+
if (raw.includes("cursor")) return "cursor";
|
|
6921
|
+
if (raw.includes("openclaw")) return "openclaw";
|
|
6922
|
+
if (raw.includes("slack")) return "slack";
|
|
6923
|
+
if (raw.includes("github")) return "github";
|
|
6924
|
+
if (raw.includes("linear")) return "linear";
|
|
6925
|
+
if (raw.includes("mcp")) return "mcp";
|
|
6926
|
+
if (raw.includes("api")) return "api";
|
|
6927
|
+
if (raw.includes("manual") || raw.includes("wizard-audit-input")) return "manual";
|
|
6928
|
+
return "unknown";
|
|
6929
|
+
}
|
|
6930
|
+
function titleFromText(text2, fallback) {
|
|
6931
|
+
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();
|
|
6932
|
+
const firstSentence = normalized.split(/[.!?]\s/)[0]?.trim() || normalized;
|
|
6933
|
+
return (firstSentence || fallback).slice(0, 160);
|
|
6934
|
+
}
|
|
6935
|
+
function findingTypeForItem(item) {
|
|
6936
|
+
switch (item.type) {
|
|
6937
|
+
case "decision":
|
|
6938
|
+
return "decision";
|
|
6939
|
+
case "artifact":
|
|
6940
|
+
return "artifact";
|
|
6941
|
+
case "open_loop":
|
|
6942
|
+
return "blocker";
|
|
6943
|
+
case "commitment":
|
|
6944
|
+
case "next_action":
|
|
6945
|
+
case "outcome":
|
|
6946
|
+
return "action";
|
|
6947
|
+
case "economics":
|
|
6948
|
+
return "business";
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
function evidenceRefFor(sourceId, index) {
|
|
6952
|
+
return `${sourceId}:derived:${index + 1}`;
|
|
6953
|
+
}
|
|
6954
|
+
function includesAny2(text2, patterns) {
|
|
6955
|
+
return patterns.some((pattern) => pattern.test(text2));
|
|
6956
|
+
}
|
|
6957
|
+
function buildCoverage(imports, connectedSources, missingSources) {
|
|
6958
|
+
const allText = imports.map((source) => source.text).join("\n").toLowerCase();
|
|
6959
|
+
const orgxObserved = /\borgx\b|useorgx|mcp__orgx__|orgx_/i.test(allText);
|
|
6960
|
+
const mcpObserved = /\bmcp\b|mcp__|tool call|tools\/call|call_tool|orgx_emit_activity/i.test(allText);
|
|
6961
|
+
const orgxMcpCalled = /mcp__orgx__|orgx_emit_activity|orgx_apply_changeset|complete_with_proof|scaffold_initiative/i.test(allText);
|
|
6962
|
+
const skillOnlySignal = orgxObserved && !orgxMcpCalled && /\bskill|instructions|agent|workflow\b/i.test(allText);
|
|
6963
|
+
return {
|
|
6964
|
+
connected: [...connectedSources],
|
|
6965
|
+
missing: [...missingSources],
|
|
6966
|
+
mcpObserved,
|
|
6967
|
+
orgxObserved,
|
|
6968
|
+
orgxMcpCalled,
|
|
6969
|
+
skillOnlySignal
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
function buildDerivedFindings(imports) {
|
|
6973
|
+
const findings = [];
|
|
6974
|
+
let derivedIndex = 0;
|
|
6975
|
+
for (const source of imports) {
|
|
6976
|
+
const sourceClient = sourceClientForImport(source);
|
|
6977
|
+
const lines = source.text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
6978
|
+
for (const line of lines) {
|
|
6979
|
+
const lower = line.toLowerCase();
|
|
6980
|
+
if (includesAny2(lower, [/\b(owner|dri|founder|teammate|customer|user|stakeholder|buyer|reviewer)\b/])) {
|
|
6981
|
+
findings.push({
|
|
6982
|
+
type: "person",
|
|
6983
|
+
title: titleFromText(line, "Person or stakeholder signal"),
|
|
6984
|
+
summary: line,
|
|
6985
|
+
source_client: sourceClient,
|
|
6986
|
+
source_id: source.sourceId,
|
|
6987
|
+
evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
|
|
6988
|
+
confidence: 0.62,
|
|
6989
|
+
metadata: { source_label: source.sourceLabel }
|
|
6990
|
+
});
|
|
6991
|
+
}
|
|
6992
|
+
if (includesAny2(lower, [/\b(product surface|surface|live room|command center|dashboard|widget|plugin|wizard|audit|onboarding|mcp|slack|github|linear)\b/])) {
|
|
6993
|
+
findings.push({
|
|
6994
|
+
type: "product_surface",
|
|
6995
|
+
title: titleFromText(line, "Product surface signal"),
|
|
6996
|
+
summary: line,
|
|
6997
|
+
source_client: sourceClient,
|
|
6998
|
+
source_id: source.sourceId,
|
|
6999
|
+
evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
|
|
7000
|
+
confidence: 0.66,
|
|
7001
|
+
metadata: { source_label: source.sourceLabel }
|
|
7002
|
+
});
|
|
7003
|
+
}
|
|
7004
|
+
if (includesAny2(lower, [/\b(goal|objective|initiative|workstream|milestone|roadmap|launch)\b/])) {
|
|
7005
|
+
findings.push({
|
|
7006
|
+
type: "goal",
|
|
7007
|
+
title: titleFromText(line, "Goal signal"),
|
|
7008
|
+
summary: line,
|
|
7009
|
+
source_client: sourceClient,
|
|
7010
|
+
source_id: source.sourceId,
|
|
7011
|
+
evidence_ref: evidenceRefFor(source.sourceId, derivedIndex++),
|
|
7012
|
+
confidence: 0.64,
|
|
7013
|
+
metadata: { source_label: source.sourceLabel }
|
|
7014
|
+
});
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
return findings;
|
|
7019
|
+
}
|
|
7020
|
+
function buildWorkGraphEvents(imports) {
|
|
7021
|
+
return imports.map((source, index) => ({
|
|
7022
|
+
schema_version: WORK_GRAPH_SCHEMA_VERSION,
|
|
7023
|
+
source_client: sourceClientForImport(source),
|
|
7024
|
+
source_id: source.sourceId,
|
|
7025
|
+
source_label: source.sourceLabel,
|
|
7026
|
+
event_type: "session_excerpt",
|
|
7027
|
+
text: source.text,
|
|
7028
|
+
evidence_ref: `${source.sourceId}:excerpt`,
|
|
7029
|
+
metadata: {
|
|
7030
|
+
import_index: index,
|
|
7031
|
+
line_count: source.text.split(/\r?\n/).filter(Boolean).length,
|
|
7032
|
+
raw_transcript_sent: false
|
|
7033
|
+
}
|
|
7034
|
+
}));
|
|
7035
|
+
}
|
|
7036
|
+
function buildWorkGraphFindings(imports) {
|
|
7037
|
+
const loopFindings = extractFounderLoopItems(imports).map((item) => {
|
|
7038
|
+
const source = imports.find((candidate) => candidate.sourceId === item.sourceId);
|
|
7039
|
+
const sourceClient = source ? sourceClientForImport(source) : "unknown";
|
|
7040
|
+
const type = findingTypeForItem(item);
|
|
7041
|
+
return {
|
|
7042
|
+
type,
|
|
7043
|
+
title: titleFromText(item.text, type),
|
|
7044
|
+
summary: item.text,
|
|
7045
|
+
source_client: sourceClient,
|
|
7046
|
+
source_id: item.sourceId,
|
|
7047
|
+
evidence_ref: item.evidenceRef,
|
|
7048
|
+
confidence: type === "decision" || type === "artifact" ? 0.82 : 0.72,
|
|
7049
|
+
metadata: {
|
|
7050
|
+
source_label: item.sourceLabel,
|
|
7051
|
+
founder_loop_type: item.type,
|
|
7052
|
+
line_number: item.lineNumber
|
|
7053
|
+
}
|
|
7054
|
+
};
|
|
7055
|
+
});
|
|
7056
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7057
|
+
return [...loopFindings, ...buildDerivedFindings(imports)].filter((finding) => {
|
|
7058
|
+
const key = `${finding.type}:${finding.source_id}:${finding.summary.toLowerCase()}`;
|
|
7059
|
+
if (seen.has(key)) return false;
|
|
7060
|
+
seen.add(key);
|
|
7061
|
+
return true;
|
|
7062
|
+
});
|
|
7063
|
+
}
|
|
7064
|
+
function buildMissedOpportunities(coverage, findings) {
|
|
7065
|
+
const opportunities = [];
|
|
7066
|
+
const hasActionableSignal = findings.some(
|
|
7067
|
+
(finding) => ["decision", "artifact", "blocker", "action"].includes(finding.type)
|
|
7068
|
+
);
|
|
7069
|
+
if (hasActionableSignal && !coverage.orgxMcpCalled) {
|
|
7070
|
+
opportunities.push({
|
|
7071
|
+
type: "missed_orchestration_opportunity",
|
|
7072
|
+
title: "High-value work happened without an OrgX MCP write",
|
|
7073
|
+
summary: "The source contains decisions, artifacts, blockers, or actions, but no durable OrgX MCP call was detected.",
|
|
7074
|
+
source_client: "wizard",
|
|
7075
|
+
source_id: "work-graph",
|
|
7076
|
+
evidence_ref: "work-graph:coverage:orgx-mcp",
|
|
7077
|
+
confidence: coverage.orgxObserved ? 0.78 : 0.7,
|
|
7078
|
+
metadata: {
|
|
7079
|
+
orgx_observed: coverage.orgxObserved,
|
|
7080
|
+
mcp_observed: coverage.mcpObserved,
|
|
7081
|
+
skill_only_signal: coverage.skillOnlySignal
|
|
7082
|
+
}
|
|
7083
|
+
});
|
|
7084
|
+
}
|
|
7085
|
+
if (coverage.missing.length > 0) {
|
|
7086
|
+
opportunities.push({
|
|
7087
|
+
type: "missed_orchestration_opportunity",
|
|
7088
|
+
title: "Source coverage is incomplete",
|
|
7089
|
+
summary: `Missing sources: ${coverage.missing.join(", ")}.`,
|
|
7090
|
+
source_client: "wizard",
|
|
7091
|
+
source_id: "work-graph",
|
|
7092
|
+
evidence_ref: "work-graph:coverage:missing",
|
|
7093
|
+
confidence: 0.76,
|
|
7094
|
+
metadata: { missing_sources: coverage.missing }
|
|
7095
|
+
});
|
|
7096
|
+
}
|
|
7097
|
+
return opportunities;
|
|
7098
|
+
}
|
|
7099
|
+
function scoreOpportunity(coverage, findings) {
|
|
7100
|
+
const count = (type) => findings.filter((finding) => finding.type === type).length;
|
|
7101
|
+
const decisions = count("decision");
|
|
7102
|
+
const artifacts = count("artifact");
|
|
7103
|
+
const blockers = count("blocker");
|
|
7104
|
+
const actions = count("action");
|
|
7105
|
+
const people = count("person");
|
|
7106
|
+
const business = count("business");
|
|
7107
|
+
const surfaces = count("product_surface");
|
|
7108
|
+
const goals = count("goal");
|
|
7109
|
+
const evidenceQuality = clampScore2(35 + Math.min(35, artifacts * 9 + decisions * 6) + Math.min(20, coverage.connected.length * 6) - coverage.missing.length * 8);
|
|
7110
|
+
const urgency = clampScore2(20 + blockers * 18 + actions * 7 + decisions * 5);
|
|
7111
|
+
const ownerClarity = clampScore2(25 + people * 18 + (findings.some((finding) => /\b(owner|dri|responsible|founder)\b/i.test(finding.summary)) ? 35 : 0));
|
|
7112
|
+
const automationPotential = clampScore2(30 + actions * 10 + surfaces * 7 + (coverage.mcpObserved ? 12 : 0));
|
|
7113
|
+
const valuePotential = clampScore2(25 + business * 20 + artifacts * 8 + goals * 10 + surfaces * 5);
|
|
7114
|
+
const orgxFit = clampScore2(35 + decisions * 8 + blockers * 10 + artifacts * 7 + (coverage.orgxObserved ? 10 : 0) + (!coverage.orgxMcpCalled ? 10 : 0));
|
|
7115
|
+
return {
|
|
7116
|
+
overall: clampScore2((valuePotential + evidenceQuality + urgency + ownerClarity + automationPotential + orgxFit) / 6),
|
|
7117
|
+
value_potential: valuePotential,
|
|
7118
|
+
evidence_quality: evidenceQuality,
|
|
7119
|
+
urgency,
|
|
7120
|
+
owner_clarity: ownerClarity,
|
|
7121
|
+
automation_potential: automationPotential,
|
|
7122
|
+
orgx_fit: orgxFit
|
|
7123
|
+
};
|
|
7124
|
+
}
|
|
7125
|
+
function inferFinalState(findings) {
|
|
7126
|
+
if (findings.some((finding) => finding.type === "blocker")) return "blocked";
|
|
7127
|
+
if (findings.some((finding) => finding.type === "artifact" || /\b(shipped|completed|done|verified)\b/i.test(finding.summary))) {
|
|
7128
|
+
return "completed";
|
|
7129
|
+
}
|
|
7130
|
+
if (findings.some((finding) => finding.type === "action" || finding.type === "decision")) return "in_progress";
|
|
7131
|
+
return "unknown";
|
|
7132
|
+
}
|
|
7133
|
+
function buildKickoffs(findings, missed, score) {
|
|
7134
|
+
const blockers = findings.filter((finding) => finding.type === "blocker").slice(0, 3);
|
|
7135
|
+
const decisions = findings.filter((finding) => finding.type === "decision").slice(0, 3);
|
|
7136
|
+
const artifacts = findings.filter((finding) => finding.type === "artifact").slice(0, 3);
|
|
7137
|
+
const kickoffs = [];
|
|
7138
|
+
if (missed.length > 0) {
|
|
7139
|
+
kickoffs.push({
|
|
7140
|
+
title: "Install continuous OrgX writeback",
|
|
7141
|
+
summary: "Turn detected work into live OrgX activity, retro, outcome, and decision records.",
|
|
7142
|
+
reason: missed[0]?.summary ?? "A source produced work without durable OrgX orchestration.",
|
|
7143
|
+
finding_refs: missed.map((finding) => finding.evidence_ref).slice(0, 4),
|
|
7144
|
+
priority: score.overall >= 75 ? "p0" : "p1"
|
|
7145
|
+
});
|
|
7146
|
+
}
|
|
7147
|
+
if (blockers.length > 0 || decisions.length > 0) {
|
|
7148
|
+
kickoffs.push({
|
|
7149
|
+
title: "Resolve the hidden decision queue",
|
|
7150
|
+
summary: "Convert unresolved decisions and blockers into owner-visible OrgX decision cards.",
|
|
7151
|
+
reason: "The Work Graph found decisions or blockers that can slow execution if they stay buried in transcripts.",
|
|
7152
|
+
finding_refs: [...blockers, ...decisions].map((finding) => finding.evidence_ref).slice(0, 5),
|
|
7153
|
+
priority: blockers.length > 0 ? "p0" : "p1"
|
|
7154
|
+
});
|
|
7155
|
+
}
|
|
7156
|
+
if (artifacts.length > 0) {
|
|
7157
|
+
kickoffs.push({
|
|
7158
|
+
title: "Attach proof to the operating loop",
|
|
7159
|
+
summary: "Promote completed work into artifacts with owners, evidence refs, and next actions.",
|
|
7160
|
+
reason: "Completed work was detected; OrgX can make it queryable and reusable.",
|
|
7161
|
+
finding_refs: artifacts.map((finding) => finding.evidence_ref),
|
|
7162
|
+
priority: "p1"
|
|
7163
|
+
});
|
|
7164
|
+
}
|
|
7165
|
+
if (kickoffs.length === 0) {
|
|
7166
|
+
kickoffs.push({
|
|
7167
|
+
title: "Connect first work source",
|
|
7168
|
+
summary: "Add AI client, Slack, or MCP source coverage so OrgX can build a useful Work Graph.",
|
|
7169
|
+
reason: "The scan did not find enough structured work to recommend a specific operating initiative.",
|
|
7170
|
+
finding_refs: [],
|
|
7171
|
+
priority: "p2"
|
|
7172
|
+
});
|
|
7173
|
+
}
|
|
7174
|
+
return kickoffs.slice(0, 3);
|
|
7175
|
+
}
|
|
7176
|
+
function entityTypeForFinding(finding) {
|
|
7177
|
+
switch (finding.type) {
|
|
7178
|
+
case "decision":
|
|
7179
|
+
return "decision";
|
|
7180
|
+
case "artifact":
|
|
7181
|
+
return "artifact";
|
|
7182
|
+
case "blocker":
|
|
7183
|
+
return "blocker";
|
|
7184
|
+
case "person":
|
|
7185
|
+
return "person";
|
|
7186
|
+
case "business":
|
|
7187
|
+
return "business";
|
|
7188
|
+
case "product_surface":
|
|
7189
|
+
return "surface";
|
|
7190
|
+
case "goal":
|
|
7191
|
+
case "initiative_candidate":
|
|
7192
|
+
return "initiative";
|
|
7193
|
+
case "action":
|
|
7194
|
+
return /\b(outcome|shipped|completed|verified)\b/i.test(finding.summary) ? "outcome" : "task";
|
|
7195
|
+
case "missed_orchestration_opportunity":
|
|
7196
|
+
return "source";
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
function trailKindForEntity(entityType) {
|
|
7200
|
+
switch (entityType) {
|
|
7201
|
+
case "decision":
|
|
7202
|
+
return "decision_trail";
|
|
7203
|
+
case "artifact":
|
|
7204
|
+
return "artifact_trail";
|
|
7205
|
+
case "person":
|
|
7206
|
+
return "person_trail";
|
|
7207
|
+
case "business":
|
|
7208
|
+
return "business_trail";
|
|
7209
|
+
case "blocker":
|
|
7210
|
+
return "blocker_trail";
|
|
7211
|
+
case "agent":
|
|
7212
|
+
return "agent_trail";
|
|
7213
|
+
case "tool":
|
|
7214
|
+
case "source":
|
|
7215
|
+
return "source_trail";
|
|
7216
|
+
case "outcome":
|
|
7217
|
+
return "outcome_trail";
|
|
7218
|
+
case "idea":
|
|
7219
|
+
return "idea_trail";
|
|
7220
|
+
case "initiative":
|
|
7221
|
+
case "workstream":
|
|
7222
|
+
case "milestone":
|
|
7223
|
+
case "task":
|
|
7224
|
+
case "surface":
|
|
7225
|
+
return "initiative_trail";
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
function eventTypeForFinding(finding) {
|
|
7229
|
+
switch (finding.type) {
|
|
7230
|
+
case "decision":
|
|
7231
|
+
return "decision_inferred";
|
|
7232
|
+
case "artifact":
|
|
7233
|
+
return /\b(verified|proof|tested|passed)\b/i.test(finding.summary) ? "artifact_verified" : "artifact_created";
|
|
7234
|
+
case "blocker":
|
|
7235
|
+
return "blocker_detected";
|
|
7236
|
+
case "person":
|
|
7237
|
+
return "owner_assigned";
|
|
7238
|
+
case "business":
|
|
7239
|
+
return "signal_detected";
|
|
7240
|
+
case "product_surface":
|
|
7241
|
+
return "signal_detected";
|
|
7242
|
+
case "goal":
|
|
7243
|
+
case "initiative_candidate":
|
|
7244
|
+
return "initiative_created";
|
|
7245
|
+
case "action":
|
|
7246
|
+
return /\b(outcome|result|impact|roi)\b/i.test(finding.summary) ? "outcome_recorded" : "recommendation_generated";
|
|
7247
|
+
case "missed_orchestration_opportunity":
|
|
7248
|
+
return "source_connected";
|
|
7249
|
+
}
|
|
7250
|
+
}
|
|
7251
|
+
function trailStateForFindings(findings) {
|
|
7252
|
+
if (findings.some((finding) => finding.type === "blocker")) return "blocked";
|
|
7253
|
+
if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "missing_evidence";
|
|
7254
|
+
if (findings.some((finding) => /\b(contradict|conflict)\b/i.test(finding.summary))) return "contradicted";
|
|
7255
|
+
if (findings.some((finding) => /\b(verified|proof|passed|complete_with_proof)\b/i.test(finding.summary))) return "verified";
|
|
7256
|
+
if (findings.some((finding) => finding.type === "decision")) return "inferred";
|
|
7257
|
+
return "observed";
|
|
7258
|
+
}
|
|
7259
|
+
function trailValenceForFindings(findings, recurrence) {
|
|
7260
|
+
if (findings.some((finding) => finding.type === "blocker")) return recurrence > 1 ? "escalating" : "risk";
|
|
7261
|
+
if (findings.some((finding) => finding.type === "missed_orchestration_opportunity")) return "leak";
|
|
7262
|
+
if (findings.some((finding) => finding.type === "decision") && recurrence > 1) return "wasteful_recurrence";
|
|
7263
|
+
if (findings.some((finding) => finding.type === "business")) return "opportunity";
|
|
7264
|
+
if (findings.some((finding) => finding.type === "artifact")) return "healthy";
|
|
7265
|
+
return recurrence > 1 ? "useful_recurrence" : "opportunity";
|
|
7266
|
+
}
|
|
7267
|
+
function trailShapeForFindings(findings, recurrence) {
|
|
7268
|
+
const decisions = findings.filter((finding) => finding.type === "decision").length;
|
|
7269
|
+
const artifacts = findings.filter((finding) => finding.type === "artifact").length;
|
|
7270
|
+
if (decisions > artifacts && decisions > 0) return "decision_heavy_artifact_light";
|
|
7271
|
+
if (artifacts > decisions && artifacts > 0 && decisions === 0) return "artifact_heavy_decision_light";
|
|
7272
|
+
if (findings.some((finding) => finding.type === "blocker")) return "accelerating_issue";
|
|
7273
|
+
if (recurrence >= 3) return "chronic_recurrence";
|
|
7274
|
+
if (findings.some((finding) => /\b(resurface|again|back|revived|reappeared)\b/i.test(finding.summary))) return "zombie_revival";
|
|
7275
|
+
if (findings.some((finding) => /\b(verified|passed|shipped|completed)\b/i.test(finding.summary))) return "healthy_execution";
|
|
7276
|
+
return "dense_recent_cluster";
|
|
7277
|
+
}
|
|
7278
|
+
function buildWorkGraphTrails(findings, generatedAt) {
|
|
7279
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7280
|
+
for (const finding of findings) {
|
|
7281
|
+
const entityType = entityTypeForFinding(finding);
|
|
7282
|
+
const key = `${entityType}:${slugPart(finding.title)}`;
|
|
7283
|
+
grouped.set(key, [...grouped.get(key) ?? [], finding]);
|
|
7284
|
+
}
|
|
7285
|
+
return [...grouped.entries()].map(([key, group], index) => {
|
|
7286
|
+
const first = group[0];
|
|
7287
|
+
const entityType = entityTypeForFinding(first);
|
|
7288
|
+
const entityId = `${entityType}:${shortHash(key, 12)}`;
|
|
7289
|
+
const trailId = `trail:${shortHash({ key, evidence: group.map((finding) => finding.evidence_ref) }, 14)}`;
|
|
7290
|
+
const recurrence = group.length;
|
|
7291
|
+
const evidenceRefs = sortedUnique(group.map((finding) => finding.evidence_ref));
|
|
7292
|
+
const events = group.map((finding, eventIndex) => ({
|
|
7293
|
+
id: `${trailId}:event:${eventIndex + 1}`,
|
|
7294
|
+
trail_id: trailId,
|
|
7295
|
+
event_type: eventTypeForFinding(finding),
|
|
7296
|
+
entity_id: entityId,
|
|
7297
|
+
entity_type: entityType,
|
|
7298
|
+
timestamp: generatedAt,
|
|
7299
|
+
source_id: finding.source_id,
|
|
7300
|
+
source_type: finding.source_client,
|
|
7301
|
+
redacted_verbatim: finding.summary.slice(0, 320),
|
|
7302
|
+
confidence: finding.confidence,
|
|
7303
|
+
evidence_refs: [finding.evidence_ref],
|
|
7304
|
+
privacy_state: "redacted"
|
|
7305
|
+
}));
|
|
7306
|
+
const edges = events.slice(1).map((event, edgeIndex) => ({
|
|
7307
|
+
id: `${trailId}:edge:${edgeIndex + 1}`,
|
|
7308
|
+
from_event_id: events[edgeIndex].id,
|
|
7309
|
+
to_event_id: event.id,
|
|
7310
|
+
relation: group.some((finding) => finding.type === "blocker") ? "blocked" : "informed",
|
|
7311
|
+
state: recurrence > 1 ? "captured" : "inferred",
|
|
7312
|
+
confidence: Math.min(0.92, Math.max(0.58, event.confidence - 0.04)),
|
|
7313
|
+
evidence_refs: event.evidence_refs
|
|
7314
|
+
}));
|
|
7315
|
+
const confidence = group.reduce((total, finding) => total + finding.confidence, 0) / group.length;
|
|
7316
|
+
return {
|
|
7317
|
+
id: trailId,
|
|
7318
|
+
kind: trailKindForEntity(entityType),
|
|
7319
|
+
title: first.title,
|
|
7320
|
+
summary: first.summary,
|
|
7321
|
+
subject_entity_id: entityId,
|
|
7322
|
+
subject_entity_type: entityType,
|
|
7323
|
+
state: trailStateForFindings(group),
|
|
7324
|
+
valence: trailValenceForFindings(group, recurrence),
|
|
7325
|
+
confidence: Number(confidence.toFixed(2)),
|
|
7326
|
+
recurrence_score: Math.min(100, recurrence * 28 + evidenceRefs.length * 6),
|
|
7327
|
+
impact_score: Math.min(100, Math.round(first.confidence * 70) + recurrence * 8 + (index < 4 ? 10 : 0)),
|
|
7328
|
+
privacy_state: "redacted",
|
|
7329
|
+
events,
|
|
7330
|
+
edges,
|
|
7331
|
+
evidence_refs: evidenceRefs,
|
|
7332
|
+
blocker_ids: group.filter((finding) => finding.type === "blocker").map((finding) => finding.evidence_ref),
|
|
7333
|
+
recommendation_ids: [],
|
|
7334
|
+
created_at: generatedAt,
|
|
7335
|
+
updated_at: generatedAt,
|
|
7336
|
+
shape: trailShapeForFindings(group, recurrence)
|
|
7337
|
+
};
|
|
7338
|
+
});
|
|
7339
|
+
}
|
|
7340
|
+
function severityFor(count) {
|
|
7341
|
+
if (count >= 6) return "critical";
|
|
7342
|
+
if (count >= 3) return "high";
|
|
7343
|
+
if (count >= 2) return "medium";
|
|
7344
|
+
return "low";
|
|
7345
|
+
}
|
|
7346
|
+
function trailsForType(trails, entityType) {
|
|
7347
|
+
return trails.filter((trail) => trail.subject_entity_type === entityType);
|
|
7348
|
+
}
|
|
7349
|
+
function buildRecurringPatterns(coverage, findings, trails) {
|
|
7350
|
+
const patterns = [];
|
|
7351
|
+
const decisionTrails = trailsForType(trails, "decision");
|
|
7352
|
+
const artifactTrails = trailsForType(trails, "artifact");
|
|
7353
|
+
const blockerTrails = trailsForType(trails, "blocker");
|
|
7354
|
+
const personTrails = trailsForType(trails, "person");
|
|
7355
|
+
const businessTrails = trailsForType(trails, "business");
|
|
7356
|
+
const highRecurrence = trails.filter((trail) => trail.recurrence_score >= 56);
|
|
7357
|
+
if (decisionTrails.length > 0 && !coverage.orgxMcpCalled) {
|
|
7358
|
+
patterns.push({
|
|
7359
|
+
id: "pattern:trapped-decision",
|
|
7360
|
+
title: "Decisions are being made without durable OrgX writeback",
|
|
7361
|
+
description: `${decisionTrails.length} decision trail${decisionTrails.length === 1 ? "" : "s"} appeared while no OrgX MCP write was detected.`,
|
|
7362
|
+
pattern_type: "trapped_decision",
|
|
7363
|
+
affected_trail_ids: decisionTrails.map((trail) => trail.id),
|
|
7364
|
+
affected_entity_ids: decisionTrails.map((trail) => trail.subject_entity_id),
|
|
7365
|
+
recurrence_count: decisionTrails.length,
|
|
7366
|
+
severity: severityFor(decisionTrails.length + 1),
|
|
7367
|
+
confidence: coverage.orgxObserved ? 0.82 : 0.74,
|
|
7368
|
+
valence: "leak",
|
|
7369
|
+
root_cause_hypothesis: "Agents and humans are making useful choices, but the runtime is not promoting those choices into organizational memory.",
|
|
7370
|
+
recommended_runtime_action_id: "recommendation:promote-decisions"
|
|
7371
|
+
});
|
|
7372
|
+
}
|
|
7373
|
+
if (artifactTrails.length > 0 && personTrails.length === 0) {
|
|
7374
|
+
patterns.push({
|
|
7375
|
+
id: "pattern:orphaned-artifact",
|
|
7376
|
+
title: "Artifacts do not have visible ownership",
|
|
7377
|
+
description: `${artifactTrails.length} artifact trail${artifactTrails.length === 1 ? "" : "s"} appeared without a clear owner trail.`,
|
|
7378
|
+
pattern_type: "orphaned_artifact",
|
|
7379
|
+
affected_trail_ids: artifactTrails.map((trail) => trail.id),
|
|
7380
|
+
affected_entity_ids: artifactTrails.map((trail) => trail.subject_entity_id),
|
|
7381
|
+
recurrence_count: artifactTrails.length,
|
|
7382
|
+
severity: severityFor(artifactTrails.length),
|
|
7383
|
+
confidence: 0.72,
|
|
7384
|
+
valence: "risk",
|
|
7385
|
+
root_cause_hypothesis: "Work is becoming concrete, but downstream accountability is not being attached at creation time.",
|
|
7386
|
+
recommended_runtime_action_id: "recommendation:assign-owner"
|
|
7387
|
+
});
|
|
7388
|
+
}
|
|
7389
|
+
if (coverage.missing.length > 0) {
|
|
7390
|
+
const sourceTrails = trailsForType(trails, "source");
|
|
7391
|
+
patterns.push({
|
|
7392
|
+
id: "pattern:missing-source",
|
|
7393
|
+
title: "Important source coverage is missing",
|
|
7394
|
+
description: `Missing sources: ${coverage.missing.join(", ")}.`,
|
|
7395
|
+
pattern_type: "missing_source",
|
|
7396
|
+
affected_trail_ids: sourceTrails.map((trail) => trail.id),
|
|
7397
|
+
affected_entity_ids: sourceTrails.map((trail) => trail.subject_entity_id),
|
|
7398
|
+
recurrence_count: coverage.missing.length,
|
|
7399
|
+
severity: severityFor(coverage.missing.length),
|
|
7400
|
+
confidence: 0.78,
|
|
7401
|
+
valence: "leak",
|
|
7402
|
+
root_cause_hypothesis: "The scan can see work happening, but coordination and proof sources are not fully connected.",
|
|
7403
|
+
recommended_runtime_action_id: "recommendation:connect-source"
|
|
7404
|
+
});
|
|
7405
|
+
}
|
|
7406
|
+
if (coverage.mcpObserved && !coverage.orgxMcpCalled) {
|
|
7407
|
+
patterns.push({
|
|
7408
|
+
id: "pattern:tooling-mismatch",
|
|
7409
|
+
title: "MCP was present, but OrgX was not called",
|
|
7410
|
+
description: "The session mentions MCP/tooling activity, but no durable OrgX MCP call was detected.",
|
|
7411
|
+
pattern_type: "tooling_mismatch",
|
|
7412
|
+
affected_trail_ids: trails.map((trail) => trail.id).slice(0, 6),
|
|
7413
|
+
affected_entity_ids: trails.map((trail) => trail.subject_entity_id).slice(0, 6),
|
|
7414
|
+
recurrence_count: findings.filter((finding) => /\bmcp|tool\b/i.test(finding.summary)).length || 1,
|
|
7415
|
+
severity: "high",
|
|
7416
|
+
confidence: 0.8,
|
|
7417
|
+
valence: "leak",
|
|
7418
|
+
root_cause_hypothesis: "The available tool layer is not automatically closing the loop when work finishes.",
|
|
7419
|
+
recommended_runtime_action_id: "recommendation:install-runtime-hooks"
|
|
7420
|
+
});
|
|
7421
|
+
}
|
|
7422
|
+
if (highRecurrence.length > 0) {
|
|
7423
|
+
patterns.push({
|
|
7424
|
+
id: "pattern:repeated-work",
|
|
7425
|
+
title: "The same work shape is recurring",
|
|
7426
|
+
description: `${highRecurrence.length} trail${highRecurrence.length === 1 ? "" : "s"} repeat strongly enough to deserve durable operating memory.`,
|
|
7427
|
+
pattern_type: "repeated_work",
|
|
7428
|
+
affected_trail_ids: highRecurrence.map((trail) => trail.id),
|
|
7429
|
+
affected_entity_ids: highRecurrence.map((trail) => trail.subject_entity_id),
|
|
7430
|
+
recurrence_count: highRecurrence.reduce((total, trail) => total + trail.events.length, 0),
|
|
7431
|
+
severity: severityFor(highRecurrence.length),
|
|
7432
|
+
confidence: 0.7,
|
|
7433
|
+
valence: "useful_recurrence",
|
|
7434
|
+
root_cause_hypothesis: "Repeated work is creating a reusable operating pattern, but it is still only implicit.",
|
|
7435
|
+
recommended_runtime_action_id: "recommendation:launch-initiative"
|
|
7436
|
+
});
|
|
7437
|
+
}
|
|
7438
|
+
if (businessTrails.length > 0 && findings.every((finding) => finding.type !== "initiative_candidate")) {
|
|
7439
|
+
patterns.push({
|
|
7440
|
+
id: "pattern:business-signal-unclaimed",
|
|
7441
|
+
title: "Business signal has not become a launchable initiative",
|
|
7442
|
+
description: `${businessTrails.length} business trail${businessTrails.length === 1 ? "" : "s"} appeared without a matching initiative candidate.`,
|
|
7443
|
+
pattern_type: "business_signal_unclaimed",
|
|
7444
|
+
affected_trail_ids: businessTrails.map((trail) => trail.id),
|
|
7445
|
+
affected_entity_ids: businessTrails.map((trail) => trail.subject_entity_id),
|
|
7446
|
+
recurrence_count: businessTrails.length,
|
|
7447
|
+
severity: severityFor(businessTrails.length),
|
|
7448
|
+
confidence: 0.68,
|
|
7449
|
+
valence: "opportunity",
|
|
7450
|
+
root_cause_hypothesis: "Revenue or ROI signal exists, but the operating graph has not turned it into an account or initiative loop.",
|
|
7451
|
+
recommended_runtime_action_id: "recommendation:launch-initiative"
|
|
7452
|
+
});
|
|
7453
|
+
}
|
|
7454
|
+
if (blockerTrails.length > 0) {
|
|
7455
|
+
patterns.push({
|
|
7456
|
+
id: "pattern:handoff-friction",
|
|
7457
|
+
title: "Blockers are becoming handoff friction",
|
|
7458
|
+
description: `${blockerTrails.length} blocker trail${blockerTrails.length === 1 ? "" : "s"} need owner-visible resolution.`,
|
|
7459
|
+
pattern_type: "handoff_friction",
|
|
7460
|
+
affected_trail_ids: blockerTrails.map((trail) => trail.id),
|
|
7461
|
+
affected_entity_ids: blockerTrails.map((trail) => trail.subject_entity_id),
|
|
7462
|
+
recurrence_count: blockerTrails.length,
|
|
7463
|
+
severity: severityFor(blockerTrails.length + 1),
|
|
7464
|
+
confidence: 0.76,
|
|
7465
|
+
valence: "risk",
|
|
7466
|
+
root_cause_hypothesis: "Execution is producing unresolved edges that need to become assigned decisions or tasks.",
|
|
7467
|
+
recommended_runtime_action_id: "recommendation:assign-owner"
|
|
7468
|
+
});
|
|
7469
|
+
}
|
|
7470
|
+
return patterns.slice(0, 8);
|
|
7471
|
+
}
|
|
7472
|
+
function buildTrailRecommendations(patterns, trails) {
|
|
7473
|
+
const recommendations = [];
|
|
7474
|
+
const add = (recommendation) => {
|
|
7475
|
+
if (!recommendations.some((existing) => existing.id === recommendation.id)) {
|
|
7476
|
+
recommendations.push(recommendation);
|
|
7477
|
+
}
|
|
7478
|
+
};
|
|
7479
|
+
for (const pattern of patterns) {
|
|
7480
|
+
const evidenceRefs = sortedUnique(
|
|
7481
|
+
trails.filter((trail) => pattern.affected_trail_ids.includes(trail.id)).flatMap((trail) => trail.evidence_refs)
|
|
7482
|
+
).slice(0, 8);
|
|
7483
|
+
if (pattern.pattern_type === "trapped_decision") {
|
|
7484
|
+
add({
|
|
7485
|
+
id: "recommendation:promote-decisions",
|
|
7486
|
+
title: "Promote trapped decisions",
|
|
7487
|
+
summary: "Turn inferred decisions into reviewed OrgX decision records with owners and downstream artifact links.",
|
|
7488
|
+
action_type: "promote_decision",
|
|
7489
|
+
trail_ids: pattern.affected_trail_ids,
|
|
7490
|
+
evidence_refs: evidenceRefs,
|
|
7491
|
+
priority: pattern.severity === "critical" || pattern.severity === "high" ? "p0" : "p1",
|
|
7492
|
+
expected_lift: "+decision durability",
|
|
7493
|
+
confidence: pattern.confidence
|
|
7494
|
+
});
|
|
7495
|
+
} else if (pattern.pattern_type === "orphaned_artifact") {
|
|
7496
|
+
add({
|
|
7497
|
+
id: "recommendation:assign-owner",
|
|
7498
|
+
title: "Assign ownership to orphaned artifacts",
|
|
7499
|
+
summary: "Attach owners and next actions to artifacts before they decay into unqueryable receipts.",
|
|
7500
|
+
action_type: "assign_owner",
|
|
7501
|
+
trail_ids: pattern.affected_trail_ids,
|
|
7502
|
+
evidence_refs: evidenceRefs,
|
|
7503
|
+
priority: "p1",
|
|
7504
|
+
expected_lift: "+owner clarity",
|
|
7505
|
+
confidence: pattern.confidence
|
|
7506
|
+
});
|
|
7507
|
+
} else if (pattern.pattern_type === "missing_source") {
|
|
7508
|
+
add({
|
|
7509
|
+
id: "recommendation:connect-source",
|
|
7510
|
+
title: "Connect missing source coverage",
|
|
7511
|
+
summary: "Close the evidence gap by connecting the coordination or proof sources where trails terminate.",
|
|
7512
|
+
action_type: "connect_source",
|
|
7513
|
+
trail_ids: pattern.affected_trail_ids,
|
|
7514
|
+
evidence_refs: evidenceRefs,
|
|
7515
|
+
priority: "p1",
|
|
7516
|
+
expected_lift: "+source confidence",
|
|
7517
|
+
confidence: pattern.confidence
|
|
7518
|
+
});
|
|
7519
|
+
} else if (pattern.pattern_type === "tooling_mismatch") {
|
|
7520
|
+
add({
|
|
7521
|
+
id: "recommendation:install-runtime-hooks",
|
|
7522
|
+
title: "Install runtime writeback hooks",
|
|
7523
|
+
summary: "Use post-session reconciliation so useful work becomes OrgX activity even when the agent forgets.",
|
|
7524
|
+
action_type: "verify_outcome",
|
|
7525
|
+
trail_ids: pattern.affected_trail_ids,
|
|
7526
|
+
evidence_refs: evidenceRefs,
|
|
7527
|
+
priority: "p0",
|
|
7528
|
+
expected_lift: "+continuous attribution",
|
|
7529
|
+
confidence: pattern.confidence
|
|
7530
|
+
});
|
|
7531
|
+
} else if (pattern.pattern_type === "business_signal_unclaimed" || pattern.pattern_type === "repeated_work") {
|
|
7532
|
+
add({
|
|
7533
|
+
id: "recommendation:launch-initiative",
|
|
7534
|
+
title: "Launch from this trail",
|
|
7535
|
+
summary: "Convert the highest-recurring evidence path into an OrgX initiative with proof requirements.",
|
|
7536
|
+
action_type: "launch_initiative",
|
|
7537
|
+
trail_ids: pattern.affected_trail_ids,
|
|
7538
|
+
evidence_refs: evidenceRefs,
|
|
7539
|
+
priority: pattern.severity === "critical" || pattern.severity === "high" ? "p0" : "p1",
|
|
7540
|
+
expected_lift: "+initiative readiness",
|
|
7541
|
+
confidence: pattern.confidence
|
|
7542
|
+
});
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
if (recommendations.length === 0 && trails.length > 0) {
|
|
7546
|
+
const topTrail = [...trails].sort((a, b) => b.impact_score - a.impact_score)[0];
|
|
7547
|
+
add({
|
|
7548
|
+
id: "recommendation:inspect-top-trail",
|
|
7549
|
+
title: "Inspect the strongest trail",
|
|
7550
|
+
summary: "Review the highest-confidence evidence path and decide whether it should become durable OrgX memory.",
|
|
7551
|
+
action_type: "launch_initiative",
|
|
7552
|
+
trail_ids: [topTrail.id],
|
|
7553
|
+
evidence_refs: topTrail.evidence_refs,
|
|
7554
|
+
priority: "p2",
|
|
7555
|
+
expected_lift: "+operating memory",
|
|
7556
|
+
confidence: topTrail.confidence
|
|
7557
|
+
});
|
|
7558
|
+
}
|
|
7559
|
+
return recommendations.slice(0, 5);
|
|
7560
|
+
}
|
|
7561
|
+
function buildWorkGraphMirror(input) {
|
|
7562
|
+
const { coverage, generatedAt, patterns, recommendations, trails } = input;
|
|
7563
|
+
const topTrail = [...trails].sort((a, b) => b.impact_score - a.impact_score)[0];
|
|
7564
|
+
const topPattern = [...patterns].sort((a, b) => b.recurrence_count - a.recurrence_count)[0];
|
|
7565
|
+
const decisionCount = trailsForType(trails, "decision").length;
|
|
7566
|
+
const artifactCount = trailsForType(trails, "artifact").length;
|
|
7567
|
+
const blockerCount = trailsForType(trails, "blocker").length;
|
|
7568
|
+
const sourceGapCount = coverage.missing.length;
|
|
7569
|
+
const headline = topPattern ? topPattern.title : topTrail ? `${topTrail.title} is the clearest work trail` : "Your work is leaving an operating trail";
|
|
7570
|
+
const primaryClaimRefs = topTrail?.evidence_refs ?? [];
|
|
7571
|
+
const claims = [
|
|
7572
|
+
{
|
|
7573
|
+
id: "mirror:trail-count",
|
|
7574
|
+
text: `${trails.length} trails were detected across ${coverage.connected.length} connected source${coverage.connected.length === 1 ? "" : "s"}.`,
|
|
7575
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 6),
|
|
7576
|
+
confidence: trails.length > 0 ? 0.82 : 0.55
|
|
7577
|
+
},
|
|
7578
|
+
{
|
|
7579
|
+
id: "mirror:decision-artifact-balance",
|
|
7580
|
+
text: `${decisionCount} decision trail${decisionCount === 1 ? "" : "s"} and ${artifactCount} artifact trail${artifactCount === 1 ? "" : "s"} were found.`,
|
|
7581
|
+
evidence_refs: trails.filter((trail) => trail.subject_entity_type === "decision" || trail.subject_entity_type === "artifact").flatMap((trail) => trail.evidence_refs).slice(0, 6),
|
|
7582
|
+
confidence: 0.78
|
|
7583
|
+
},
|
|
7584
|
+
{
|
|
7585
|
+
id: "mirror:missing-sources",
|
|
7586
|
+
text: sourceGapCount > 0 ? `${sourceGapCount} source gap${sourceGapCount === 1 ? "" : "s"} still limit attribution depth.` : "The connected sources are enough for a first operating profile.",
|
|
7587
|
+
evidence_refs: trailsForType(trails, "source").flatMap((trail) => trail.evidence_refs).slice(0, 4),
|
|
7588
|
+
confidence: sourceGapCount > 0 ? 0.76 : 0.66
|
|
7589
|
+
}
|
|
7590
|
+
];
|
|
7591
|
+
const body = [
|
|
7592
|
+
`OrgX found ${trails.length} evidence trail${trails.length === 1 ? "" : "s"} across ${coverage.connected.join(", ") || "local sources"}.`,
|
|
7593
|
+
`The strongest signal is ${topPattern ? topPattern.title.toLowerCase() : topTrail?.title ?? "still forming"}.`,
|
|
7594
|
+
blockerCount > 0 ? `${blockerCount} blocker trail${blockerCount === 1 ? "" : "s"} need promotion into owner-visible work.` : "The healthiest trails already connect evidence to action.",
|
|
7595
|
+
recommendations[0] ? `The next durable move is: ${recommendations[0].title}.` : "The next move is to inspect the highest-confidence trail before publishing it."
|
|
7596
|
+
].join(" ");
|
|
7597
|
+
return {
|
|
7598
|
+
headline,
|
|
7599
|
+
body,
|
|
7600
|
+
lens: "all",
|
|
7601
|
+
claims,
|
|
7602
|
+
...topTrail ? { primary_trail_id: topTrail.id } : {},
|
|
7603
|
+
generated_at: generatedAt
|
|
7604
|
+
};
|
|
7605
|
+
}
|
|
7606
|
+
function buildTensionMetrics(input) {
|
|
7607
|
+
const { coverage, patterns, trails } = input;
|
|
7608
|
+
const decisionTrails = trailsForType(trails, "decision");
|
|
7609
|
+
const blockerTrails = trailsForType(trails, "blocker");
|
|
7610
|
+
const artifactTrails = trailsForType(trails, "artifact");
|
|
7611
|
+
const missingSourceTrails = trailsForType(trails, "source");
|
|
7612
|
+
const topReady = trails.filter((trail) => trail.impact_score >= 70 && trail.confidence >= 0.75);
|
|
7613
|
+
return [
|
|
7614
|
+
{
|
|
7615
|
+
id: "tension:work-leaks",
|
|
7616
|
+
label: "work leaks",
|
|
7617
|
+
value: String(patterns.filter((pattern) => pattern.valence === "leak" || pattern.valence === "risk").length),
|
|
7618
|
+
tone: patterns.some((pattern) => pattern.severity === "critical" || pattern.severity === "high") ? "danger" : "warning",
|
|
7619
|
+
trail_ids: patterns.flatMap((pattern) => pattern.affected_trail_ids).slice(0, 8),
|
|
7620
|
+
evidence_refs: trails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
7621
|
+
explanation: "Patterns where evidence exists but ownership, source coverage, or writeback is incomplete."
|
|
7622
|
+
},
|
|
7623
|
+
{
|
|
7624
|
+
id: "tension:decisions-decaying",
|
|
7625
|
+
label: "decisions decaying",
|
|
7626
|
+
value: String(decisionTrails.filter((trail) => trail.state === "inferred" || trail.state === "missing_evidence").length),
|
|
7627
|
+
tone: decisionTrails.length > 0 && !coverage.orgxMcpCalled ? "danger" : "muted",
|
|
7628
|
+
trail_ids: decisionTrails.map((trail) => trail.id),
|
|
7629
|
+
evidence_refs: decisionTrails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
7630
|
+
explanation: "Decision trails that have not been promoted into durable OrgX records."
|
|
7631
|
+
},
|
|
7632
|
+
{
|
|
7633
|
+
id: "tension:artifacts-orphaned",
|
|
7634
|
+
label: "artifacts orphaned",
|
|
7635
|
+
value: String(artifactTrails.filter((trail) => trail.state !== "verified").length),
|
|
7636
|
+
tone: artifactTrails.some((trail) => trail.shape === "artifact_heavy_decision_light") ? "warning" : "muted",
|
|
7637
|
+
trail_ids: artifactTrails.map((trail) => trail.id),
|
|
7638
|
+
evidence_refs: artifactTrails.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
7639
|
+
explanation: "Artifacts that exist without complete decision, verification, or owner context."
|
|
7640
|
+
},
|
|
7641
|
+
{
|
|
7642
|
+
id: "tension:sources-missing",
|
|
7643
|
+
label: "sources missing",
|
|
7644
|
+
value: String(coverage.missing.length),
|
|
7645
|
+
tone: coverage.missing.length > 0 ? "warning" : "good",
|
|
7646
|
+
trail_ids: missingSourceTrails.map((trail) => trail.id),
|
|
7647
|
+
evidence_refs: missingSourceTrails.flatMap((trail) => trail.evidence_refs).slice(0, 6),
|
|
7648
|
+
explanation: "Disconnected coordination or proof sources that limit attribution confidence."
|
|
7649
|
+
},
|
|
7650
|
+
{
|
|
7651
|
+
id: "tension:launch-ready",
|
|
7652
|
+
label: "launch ready",
|
|
7653
|
+
value: String(topReady.length),
|
|
7654
|
+
tone: topReady.length > 0 && blockerTrails.length === 0 ? "good" : "muted",
|
|
7655
|
+
trail_ids: topReady.map((trail) => trail.id).slice(0, 6),
|
|
7656
|
+
evidence_refs: topReady.flatMap((trail) => trail.evidence_refs).slice(0, 8),
|
|
7657
|
+
explanation: "High-confidence trails that can become initiatives, decisions, artifacts, or owner-visible follow-ups."
|
|
7658
|
+
}
|
|
7659
|
+
];
|
|
7660
|
+
}
|
|
7661
|
+
function countFindingsByType(findings) {
|
|
7662
|
+
const counts = {};
|
|
7663
|
+
for (const finding of findings) {
|
|
7664
|
+
counts[finding.type] = (counts[finding.type] ?? 0) + 1;
|
|
7665
|
+
}
|
|
7666
|
+
return Object.fromEntries(
|
|
7667
|
+
Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
|
|
7668
|
+
);
|
|
7669
|
+
}
|
|
7670
|
+
function buildWorkGraphFingerprint(input) {
|
|
7671
|
+
const sourceClients = sortedUnique(input.findings.map((finding) => finding.source_client));
|
|
7672
|
+
const patternHashes = input.findings.map(
|
|
7673
|
+
(finding) => shortHash({
|
|
7674
|
+
type: finding.type,
|
|
7675
|
+
title: normalizeFingerprintText(finding.title),
|
|
7676
|
+
summary: normalizeFingerprintText(finding.summary),
|
|
7677
|
+
source_client: finding.source_client
|
|
7678
|
+
})
|
|
7679
|
+
).sort();
|
|
7680
|
+
const kickoffHashes = input.kickoffs.map(
|
|
7681
|
+
(kickoff) => shortHash({
|
|
7682
|
+
title: normalizeFingerprintText(kickoff.title),
|
|
7683
|
+
summary: normalizeFingerprintText(kickoff.summary),
|
|
7684
|
+
priority: kickoff.priority
|
|
7685
|
+
})
|
|
7686
|
+
).sort();
|
|
7687
|
+
const trailShapeHashes = input.trails.map(
|
|
7688
|
+
(trail) => shortHash({
|
|
7689
|
+
kind: trail.kind,
|
|
7690
|
+
subject_entity_type: trail.subject_entity_type,
|
|
7691
|
+
state: trail.state,
|
|
7692
|
+
valence: trail.valence,
|
|
7693
|
+
shape: trail.shape,
|
|
7694
|
+
title: normalizeFingerprintText(trail.title)
|
|
7695
|
+
})
|
|
7696
|
+
).sort();
|
|
7697
|
+
const recurringPatternHashes = input.recurringPatterns.map(
|
|
7698
|
+
(pattern) => shortHash({
|
|
7699
|
+
pattern_type: pattern.pattern_type,
|
|
7700
|
+
severity: pattern.severity,
|
|
7701
|
+
title: normalizeFingerprintText(pattern.title),
|
|
7702
|
+
recurrence_count: pattern.recurrence_count
|
|
7703
|
+
})
|
|
7704
|
+
).sort();
|
|
7705
|
+
const basis = {
|
|
7706
|
+
schema_version: WORK_GRAPH_SCHEMA_VERSION,
|
|
7707
|
+
fingerprint_version: WORK_GRAPH_FINGERPRINT_VERSION,
|
|
7708
|
+
workspace_hash: shortHash({
|
|
7709
|
+
id: normalizeFingerprintText(input.workspace.id),
|
|
7710
|
+
name: normalizeFingerprintText(input.workspace.name)
|
|
7711
|
+
}),
|
|
7712
|
+
source_clients: sourceClients,
|
|
7713
|
+
connected_source_hashes: sortedUnique(
|
|
7714
|
+
input.connectedSources.map((source) => shortHash(normalizeFingerprintText(source)))
|
|
7715
|
+
),
|
|
7716
|
+
missing_source_hashes: sortedUnique(
|
|
7717
|
+
input.missingSources.map((source) => shortHash(normalizeFingerprintText(source)))
|
|
7718
|
+
),
|
|
7719
|
+
finding_type_counts: countFindingsByType(input.findings),
|
|
7720
|
+
pattern_hashes: patternHashes,
|
|
7721
|
+
trail_shape_hashes: trailShapeHashes,
|
|
7722
|
+
recurring_pattern_hashes: recurringPatternHashes,
|
|
7723
|
+
kickoff_hashes: kickoffHashes,
|
|
7724
|
+
raw_transcripts_included: false
|
|
7725
|
+
};
|
|
7726
|
+
const fingerprint = `wgf_${hashJson(basis).slice(0, 24)}`;
|
|
7727
|
+
return {
|
|
7728
|
+
fingerprint,
|
|
7729
|
+
basis,
|
|
7730
|
+
hydration: {
|
|
7731
|
+
strategy: "work_graph_fingerprint_claim",
|
|
7732
|
+
hydration_key: `orgx:work-graph:${fingerprint}`,
|
|
7733
|
+
eligible: input.findings.length > 0,
|
|
7734
|
+
notes: [
|
|
7735
|
+
"Use this fingerprint to claim the pre-signup Work Graph after account creation.",
|
|
7736
|
+
"The fingerprint is derived from normalized patterns, source coverage, and kickoff shapes, not raw transcripts."
|
|
7737
|
+
]
|
|
7738
|
+
}
|
|
7739
|
+
};
|
|
7740
|
+
}
|
|
7741
|
+
function buildSessionReconciliationReport(input) {
|
|
7742
|
+
if (input.imports.length === 0) {
|
|
7743
|
+
throw new Error("At least one source import is required to build a Work Graph report.");
|
|
7744
|
+
}
|
|
7745
|
+
const generatedAt = input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7746
|
+
const connectedSources = input.connectedSources ?? input.imports.map((source) => source.sourceLabel);
|
|
7747
|
+
const missingSources = input.missingSources ?? [];
|
|
7748
|
+
const coverage = buildCoverage(input.imports, connectedSources, missingSources);
|
|
7749
|
+
const events = buildWorkGraphEvents(input.imports);
|
|
7750
|
+
const findings = buildWorkGraphFindings(input.imports);
|
|
7751
|
+
const missed = buildMissedOpportunities(coverage, findings);
|
|
7752
|
+
const allFindings = [...findings, ...missed];
|
|
7753
|
+
const trails = buildWorkGraphTrails(allFindings, generatedAt);
|
|
7754
|
+
const recurringPatterns = buildRecurringPatterns(coverage, allFindings, trails);
|
|
7755
|
+
const opportunityScore = scoreOpportunity(coverage, allFindings);
|
|
7756
|
+
const initiativeKickoffs = buildKickoffs(allFindings, missed, opportunityScore);
|
|
7757
|
+
const recommendations = buildTrailRecommendations(recurringPatterns, trails);
|
|
7758
|
+
const mirror = buildWorkGraphMirror({
|
|
7759
|
+
coverage,
|
|
7760
|
+
generatedAt,
|
|
7761
|
+
patterns: recurringPatterns,
|
|
7762
|
+
recommendations,
|
|
7763
|
+
trails
|
|
7764
|
+
});
|
|
7765
|
+
const tensionMetrics = buildTensionMetrics({
|
|
7766
|
+
coverage,
|
|
7767
|
+
patterns: recurringPatterns,
|
|
7768
|
+
trails
|
|
7769
|
+
});
|
|
7770
|
+
const fingerprint = buildWorkGraphFingerprint({
|
|
7771
|
+
connectedSources,
|
|
7772
|
+
findings: allFindings,
|
|
7773
|
+
kickoffs: initiativeKickoffs,
|
|
7774
|
+
missingSources,
|
|
7775
|
+
recurringPatterns,
|
|
7776
|
+
trails,
|
|
7777
|
+
workspace: input.workspace
|
|
7778
|
+
});
|
|
7779
|
+
const reportSeed = {
|
|
7780
|
+
generatedAt,
|
|
7781
|
+
imports: input.imports.map((source) => ({
|
|
7782
|
+
sourceId: source.sourceId,
|
|
7783
|
+
textHash: hashJson(source.text)
|
|
7784
|
+
})),
|
|
7785
|
+
workspace: input.workspace
|
|
7786
|
+
};
|
|
7787
|
+
const reportHash = hashJson(reportSeed);
|
|
7788
|
+
const sessionId = input.sessionId ?? `work-graph-${reportHash.slice(0, 16)}`;
|
|
7789
|
+
return {
|
|
7790
|
+
schema_version: WORK_GRAPH_SCHEMA_VERSION,
|
|
7791
|
+
report_id: reportHash.slice(0, 24),
|
|
7792
|
+
idempotency_key: `work-graph:${sessionId}:${reportHash.slice(0, 16)}`,
|
|
7793
|
+
work_graph_fingerprint: fingerprint.fingerprint,
|
|
7794
|
+
fingerprint_version: WORK_GRAPH_FINGERPRINT_VERSION,
|
|
7795
|
+
fingerprint_basis: fingerprint.basis,
|
|
7796
|
+
signup_hydration: fingerprint.hydration,
|
|
7797
|
+
generated_at: generatedAt,
|
|
7798
|
+
source_client: "wizard",
|
|
7799
|
+
session_id: sessionId,
|
|
7800
|
+
workspace: input.workspace,
|
|
7801
|
+
source_coverage: coverage,
|
|
7802
|
+
final_state: inferFinalState(allFindings),
|
|
7803
|
+
events,
|
|
7804
|
+
findings: allFindings,
|
|
7805
|
+
missed_orchestration_opportunities: missed,
|
|
7806
|
+
trails,
|
|
7807
|
+
recurring_patterns: recurringPatterns,
|
|
7808
|
+
recommendations,
|
|
7809
|
+
mirror,
|
|
7810
|
+
tension_metrics: tensionMetrics,
|
|
7811
|
+
opportunity_score: opportunityScore,
|
|
7812
|
+
initiative_kickoffs: initiativeKickoffs,
|
|
7813
|
+
redaction_level: "summary_only",
|
|
7814
|
+
raw_transcripts_sent: false
|
|
7815
|
+
};
|
|
7816
|
+
}
|
|
7817
|
+
function renderWorkGraphMarkdown(report) {
|
|
7818
|
+
const lines = [];
|
|
7819
|
+
lines.push("# OrgX Work Graph Reconciliation");
|
|
7820
|
+
lines.push("");
|
|
7821
|
+
lines.push(`Generated: ${report.generated_at}`);
|
|
7822
|
+
lines.push(`Workspace: ${report.workspace.name} (${report.workspace.id})`);
|
|
7823
|
+
lines.push(`Report ID: ${report.report_id}`);
|
|
7824
|
+
lines.push(`Work graph fingerprint: ${report.work_graph_fingerprint}`);
|
|
7825
|
+
lines.push(`Hydration key: ${report.signup_hydration.hydration_key}`);
|
|
7826
|
+
lines.push(`Final state: ${report.final_state}`);
|
|
7827
|
+
lines.push("");
|
|
7828
|
+
lines.push("## Opportunity Score");
|
|
7829
|
+
lines.push("");
|
|
7830
|
+
lines.push(`Overall: ${report.opportunity_score.overall}/100`);
|
|
7831
|
+
lines.push(`Value potential: ${report.opportunity_score.value_potential}/100`);
|
|
7832
|
+
lines.push(`Evidence quality: ${report.opportunity_score.evidence_quality}/100`);
|
|
7833
|
+
lines.push(`Urgency: ${report.opportunity_score.urgency}/100`);
|
|
7834
|
+
lines.push(`Owner clarity: ${report.opportunity_score.owner_clarity}/100`);
|
|
7835
|
+
lines.push(`Automation potential: ${report.opportunity_score.automation_potential}/100`);
|
|
7836
|
+
lines.push(`OrgX fit: ${report.opportunity_score.orgx_fit}/100`);
|
|
7837
|
+
lines.push("");
|
|
7838
|
+
lines.push("## Source Coverage");
|
|
7839
|
+
lines.push("");
|
|
7840
|
+
lines.push(`Connected: ${report.source_coverage.connected.join(", ") || "none"}`);
|
|
7841
|
+
lines.push(`Missing: ${report.source_coverage.missing.join(", ") || "none"}`);
|
|
7842
|
+
lines.push(`MCP observed: ${report.source_coverage.mcpObserved ? "yes" : "no"}`);
|
|
7843
|
+
lines.push(`OrgX observed: ${report.source_coverage.orgxObserved ? "yes" : "no"}`);
|
|
7844
|
+
lines.push(`OrgX MCP called: ${report.source_coverage.orgxMcpCalled ? "yes" : "no"}`);
|
|
7845
|
+
lines.push("");
|
|
7846
|
+
lines.push("## Mirror");
|
|
7847
|
+
lines.push("");
|
|
7848
|
+
lines.push(`### ${report.mirror.headline}`);
|
|
7849
|
+
lines.push("");
|
|
7850
|
+
lines.push(report.mirror.body);
|
|
7851
|
+
lines.push("");
|
|
7852
|
+
for (const claim of report.mirror.claims) {
|
|
7853
|
+
lines.push(`- ${claim.text} (${claim.evidence_refs.join(", ") || "no evidence refs"})`);
|
|
7854
|
+
}
|
|
7855
|
+
lines.push("");
|
|
7856
|
+
lines.push("## Live Tension");
|
|
7857
|
+
lines.push("");
|
|
7858
|
+
for (const metric of report.tension_metrics) {
|
|
7859
|
+
lines.push(`- ${metric.value} ${metric.label}: ${metric.explanation}`);
|
|
7860
|
+
}
|
|
7861
|
+
lines.push("");
|
|
7862
|
+
lines.push("## Work Graph Trails");
|
|
7863
|
+
lines.push("");
|
|
7864
|
+
for (const trail of report.trails.slice(0, 12)) {
|
|
7865
|
+
lines.push(`- [${trail.kind}] ${trail.title} \u2014 ${trail.state}, ${trail.valence}, ${trail.shape} (${trail.evidence_refs.join(", ")})`);
|
|
7866
|
+
}
|
|
7867
|
+
lines.push("");
|
|
7868
|
+
lines.push("## Recurring Patterns");
|
|
7869
|
+
lines.push("");
|
|
7870
|
+
if (report.recurring_patterns.length === 0) {
|
|
7871
|
+
lines.push("- No recurring patterns detected yet.");
|
|
7872
|
+
} else {
|
|
7873
|
+
for (const pattern of report.recurring_patterns) {
|
|
7874
|
+
lines.push(`- [${pattern.severity}] ${pattern.title}: ${pattern.root_cause_hypothesis}`);
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
lines.push("");
|
|
7878
|
+
lines.push("## Top Findings");
|
|
7879
|
+
lines.push("");
|
|
7880
|
+
for (const finding of report.findings.slice(0, 12)) {
|
|
7881
|
+
lines.push(`- [${finding.type}] ${finding.title} (${finding.evidence_ref})`);
|
|
7882
|
+
}
|
|
7883
|
+
lines.push("");
|
|
7884
|
+
lines.push("## Missed Orchestration");
|
|
7885
|
+
lines.push("");
|
|
7886
|
+
if (report.missed_orchestration_opportunities.length === 0) {
|
|
7887
|
+
lines.push("- No missed orchestration opportunities detected.");
|
|
7888
|
+
} else {
|
|
7889
|
+
for (const finding of report.missed_orchestration_opportunities) {
|
|
7890
|
+
lines.push(`- ${finding.title}: ${finding.summary}`);
|
|
7891
|
+
}
|
|
7892
|
+
}
|
|
7893
|
+
lines.push("");
|
|
7894
|
+
lines.push("## Initiative Kickoffs");
|
|
7895
|
+
lines.push("");
|
|
7896
|
+
for (const kickoff of report.initiative_kickoffs) {
|
|
7897
|
+
lines.push(`- [${kickoff.priority}] ${kickoff.title}: ${kickoff.summary}`);
|
|
7898
|
+
}
|
|
7899
|
+
lines.push("");
|
|
7900
|
+
lines.push("## Eject Bay Recommendations");
|
|
7901
|
+
lines.push("");
|
|
7902
|
+
for (const recommendation of report.recommendations) {
|
|
7903
|
+
lines.push(`- [${recommendation.priority}] ${recommendation.title}: ${recommendation.summary}`);
|
|
7904
|
+
}
|
|
7905
|
+
lines.push("");
|
|
7906
|
+
lines.push("## Signup Hydration");
|
|
7907
|
+
lines.push("");
|
|
7908
|
+
lines.push(`- Strategy: ${report.signup_hydration.strategy}`);
|
|
7909
|
+
lines.push(`- Eligible: ${report.signup_hydration.eligible ? "yes" : "no"}`);
|
|
7910
|
+
for (const note of report.signup_hydration.notes) {
|
|
7911
|
+
lines.push(`- ${note}`);
|
|
7912
|
+
}
|
|
7913
|
+
lines.push("");
|
|
7914
|
+
lines.push("## Privacy");
|
|
7915
|
+
lines.push("");
|
|
7916
|
+
lines.push("- Raw transcripts were not sent.");
|
|
7917
|
+
lines.push("- This report contains summaries, hashes, and evidence refs only.");
|
|
7918
|
+
return lines.join("\n");
|
|
7919
|
+
}
|
|
7920
|
+
|
|
7921
|
+
// src/lib/runtime-hooks.ts
|
|
7922
|
+
import { copyFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
7923
|
+
import { homedir as homedir2 } from "os";
|
|
7924
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
7925
|
+
var HOOK_MARKER = "orgx-session-hook.mjs";
|
|
7926
|
+
var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
|
|
7927
|
+
var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
|
|
7928
|
+
function defaultPaths(options = {}) {
|
|
7929
|
+
const hookDir = join5(ORGX_WIZARD_CONFIG_HOME, "hooks");
|
|
7930
|
+
return {
|
|
7931
|
+
claudeSettingsPath: options.claudeSettingsPath ?? join5(CLAUDE_DIR, "settings.json"),
|
|
7932
|
+
codexConfigPath: options.codexConfigPath ?? CODEX_CONFIG_PATH ?? join5(CODEX_DIR, "config.toml"),
|
|
7933
|
+
codexHooksPath: options.codexHooksPath ?? join5(CODEX_DIR, "hooks.json"),
|
|
7934
|
+
hookScriptPath: options.hookScriptPath ?? join5(hookDir, HOOK_MARKER),
|
|
7935
|
+
outboxPath: options.outboxPath ?? join5(hookDir, "events.jsonl")
|
|
7936
|
+
};
|
|
7937
|
+
}
|
|
7938
|
+
function countJsonlLines(path) {
|
|
7939
|
+
const raw = readTextIfExists(path);
|
|
7940
|
+
if (!raw) return 0;
|
|
7941
|
+
return raw.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
|
|
7942
|
+
}
|
|
7943
|
+
function backupPath(path, now) {
|
|
7944
|
+
const timestamp = now.toISOString().replace(/[:.]/g, "-");
|
|
7945
|
+
return `${path}.bak.${timestamp}`;
|
|
7946
|
+
}
|
|
7947
|
+
function backupExisting(path, now) {
|
|
7948
|
+
if (!existsSync6(path)) return null;
|
|
7949
|
+
const backup = backupPath(path, now);
|
|
7950
|
+
copyFileSync(path, backup);
|
|
7951
|
+
return backup;
|
|
7952
|
+
}
|
|
7953
|
+
function hasOrgxHook(raw) {
|
|
7954
|
+
return Boolean(raw?.includes(HOOK_MARKER));
|
|
7955
|
+
}
|
|
7956
|
+
function codexHooksEnabled(raw) {
|
|
7957
|
+
return Boolean(raw && /^\s*codex_hooks\s*=\s*true\s*$/m.test(raw));
|
|
7958
|
+
}
|
|
7959
|
+
function codexHasNotify(raw) {
|
|
7960
|
+
return Boolean(raw && /^\s*notify\s*=/m.test(raw));
|
|
7961
|
+
}
|
|
7962
|
+
function buildRuntimeHookScriptContent() {
|
|
7963
|
+
return `#!/usr/bin/env node
|
|
7964
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
7965
|
+
import { dirname, join } from "node:path";
|
|
7966
|
+
import { homedir } from "node:os";
|
|
7967
|
+
|
|
7968
|
+
function parseArgs(argv) {
|
|
7969
|
+
const args = {};
|
|
7970
|
+
for (const arg of argv) {
|
|
7971
|
+
if (!arg.startsWith("--")) continue;
|
|
7972
|
+
const [key, ...rest] = arg.slice(2).split("=");
|
|
7973
|
+
args[key] = rest.length > 0 ? rest.join("=") : "true";
|
|
7974
|
+
}
|
|
7975
|
+
return args;
|
|
7976
|
+
}
|
|
7977
|
+
|
|
7978
|
+
function pickString(...values) {
|
|
7979
|
+
for (const value of values) {
|
|
7980
|
+
if (typeof value !== "string") continue;
|
|
7981
|
+
const trimmed = value.trim();
|
|
7982
|
+
if (trimmed) return trimmed;
|
|
7983
|
+
}
|
|
7984
|
+
return undefined;
|
|
7985
|
+
}
|
|
7986
|
+
|
|
7987
|
+
async function readStdin() {
|
|
7988
|
+
const chunks = [];
|
|
7989
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
7990
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
7991
|
+
}
|
|
7992
|
+
|
|
7993
|
+
function parseJson(value) {
|
|
7994
|
+
try {
|
|
7995
|
+
const parsed = JSON.parse(value || "{}");
|
|
7996
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
7997
|
+
} catch {
|
|
7998
|
+
return {};
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
|
|
8002
|
+
function summarize(payload) {
|
|
8003
|
+
const toolName = pickString(payload.tool_name, payload.toolName, payload.tool?.name, payload.name);
|
|
8004
|
+
const prompt = pickString(payload.prompt);
|
|
8005
|
+
return {
|
|
8006
|
+
tool_name: toolName,
|
|
8007
|
+
prompt_chars: prompt ? prompt.length : undefined,
|
|
8008
|
+
payload_keys: Object.keys(payload).slice(0, 40),
|
|
8009
|
+
};
|
|
8010
|
+
}
|
|
8011
|
+
|
|
8012
|
+
const args = parseArgs(process.argv.slice(2));
|
|
8013
|
+
const raw = await readStdin();
|
|
8014
|
+
const payload = parseJson(raw);
|
|
8015
|
+
const outbox = pickString(
|
|
8016
|
+
process.env.ORGX_WIZARD_HOOK_OUTBOX,
|
|
8017
|
+
args.outbox,
|
|
8018
|
+
join(homedir(), ".config", "useorgx", "wizard", "hooks", "events.jsonl")
|
|
8019
|
+
);
|
|
8020
|
+
const event = pickString(args.event, payload.hook_event_name, payload.hookEventName, payload.event, payload.eventName, "unknown");
|
|
8021
|
+
const sourceClient = pickString(args.source_client, args["source-client"], "unknown");
|
|
8022
|
+
|
|
8023
|
+
const record = {
|
|
8024
|
+
schema_version: "2026-05-07",
|
|
8025
|
+
source: "orgx_wizard_runtime_hook",
|
|
8026
|
+
source_client: sourceClient,
|
|
8027
|
+
event,
|
|
8028
|
+
session_id: pickString(payload.session_id, payload.sessionId, payload.conversation_id, payload.conversationId),
|
|
8029
|
+
turn_id: pickString(payload.turn_id, payload.turnId),
|
|
8030
|
+
cwd: pickString(payload.cwd, payload.working_directory, payload.workspace, process.cwd()),
|
|
8031
|
+
transcript_path: pickString(payload.transcript_path, payload.transcriptPath),
|
|
8032
|
+
timestamp: new Date().toISOString(),
|
|
8033
|
+
summary: summarize(payload),
|
|
8034
|
+
};
|
|
8035
|
+
|
|
8036
|
+
try {
|
|
8037
|
+
mkdirSync(dirname(outbox), { recursive: true, mode: 0o700 });
|
|
8038
|
+
appendFileSync(outbox, JSON.stringify(record) + "\\n", { encoding: "utf8", mode: 0o600 });
|
|
8039
|
+
} catch {
|
|
8040
|
+
// Hooks must never break the user's agent runtime.
|
|
8041
|
+
}
|
|
8042
|
+
|
|
8043
|
+
process.exit(0);
|
|
8044
|
+
`;
|
|
8045
|
+
}
|
|
8046
|
+
function buildHookCommand(params) {
|
|
8047
|
+
return [
|
|
8048
|
+
"node",
|
|
8049
|
+
JSON.stringify(params.hookScriptPath),
|
|
8050
|
+
`--event=${params.event}`,
|
|
8051
|
+
`--source_client=${params.sourceClient}`,
|
|
8052
|
+
`--outbox=${params.outboxPath}`
|
|
8053
|
+
].join(" ");
|
|
8054
|
+
}
|
|
8055
|
+
function mergeCodexHooks(raw, paths) {
|
|
8056
|
+
const value = parseJsonObject(raw);
|
|
8057
|
+
const hooks = isRecord(value.hooks) ? value.hooks : {};
|
|
8058
|
+
let changed = false;
|
|
8059
|
+
for (const event of HOOK_EVENTS) {
|
|
8060
|
+
const existing = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
8061
|
+
const command = buildHookCommand({
|
|
8062
|
+
event,
|
|
8063
|
+
hookScriptPath: paths.hookScriptPath,
|
|
8064
|
+
outboxPath: paths.outboxPath,
|
|
8065
|
+
sourceClient: "codex"
|
|
8066
|
+
});
|
|
8067
|
+
const already = existing.some(
|
|
8068
|
+
(entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
|
|
8069
|
+
);
|
|
8070
|
+
if (!already) {
|
|
8071
|
+
hooks[event] = [...existing, { command }];
|
|
8072
|
+
changed = true;
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
value.hooks = hooks;
|
|
8076
|
+
return { changed: changed || !raw, value };
|
|
8077
|
+
}
|
|
8078
|
+
function mergeClaudeHooks(raw, paths) {
|
|
8079
|
+
const value = parseJsonObject(raw);
|
|
8080
|
+
const hooksRoot = isRecord(value.hooks) ? value.hooks : {};
|
|
8081
|
+
let changed = false;
|
|
8082
|
+
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
8083
|
+
const list = Array.isArray(hooksRoot[event]) ? hooksRoot[event] : [];
|
|
8084
|
+
const matcher = event === "PreToolUse" || event === "PostToolUse" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
|
|
8085
|
+
const command = buildHookCommand({
|
|
8086
|
+
event,
|
|
8087
|
+
hookScriptPath: paths.hookScriptPath,
|
|
8088
|
+
outboxPath: paths.outboxPath,
|
|
8089
|
+
sourceClient: "claude-code"
|
|
8090
|
+
});
|
|
8091
|
+
let rule = list.find((entry) => isRecord(entry) && entry.matcher === matcher);
|
|
8092
|
+
if (!rule) {
|
|
8093
|
+
rule = { matcher, hooks: [] };
|
|
8094
|
+
list.push(rule);
|
|
8095
|
+
changed = true;
|
|
8096
|
+
}
|
|
8097
|
+
const hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
|
|
8098
|
+
const already = hooks.some(
|
|
8099
|
+
(entry) => isRecord(entry) && entry.type === "command" && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
|
|
8100
|
+
);
|
|
8101
|
+
if (!already) {
|
|
8102
|
+
hooks.push({ type: "command", command });
|
|
8103
|
+
rule.hooks = hooks;
|
|
8104
|
+
changed = true;
|
|
8105
|
+
}
|
|
8106
|
+
hooksRoot[event] = list;
|
|
8107
|
+
}
|
|
8108
|
+
value.hooks = hooksRoot;
|
|
8109
|
+
return { changed: changed || !raw, value };
|
|
8110
|
+
}
|
|
8111
|
+
function ensureCodexHooksFeature(raw) {
|
|
8112
|
+
const current = raw ?? "";
|
|
8113
|
+
if (codexHooksEnabled(current)) {
|
|
8114
|
+
return { changed: false, value: current };
|
|
8115
|
+
}
|
|
8116
|
+
if (/^\s*codex_hooks\s*=\s*false\s*$/m.test(current)) {
|
|
8117
|
+
return {
|
|
8118
|
+
changed: true,
|
|
8119
|
+
value: current.replace(/^\s*codex_hooks\s*=\s*false\s*$/m, "codex_hooks = true")
|
|
8120
|
+
};
|
|
8121
|
+
}
|
|
8122
|
+
if (/^\s*\[features\]\s*$/m.test(current)) {
|
|
8123
|
+
return {
|
|
8124
|
+
changed: true,
|
|
8125
|
+
value: current.replace(/^(\s*\[features\]\s*)$/m, "$1\ncodex_hooks = true")
|
|
8126
|
+
};
|
|
8127
|
+
}
|
|
8128
|
+
const suffix = current.trimEnd().length > 0 ? "\n\n" : "";
|
|
8129
|
+
return {
|
|
8130
|
+
changed: true,
|
|
8131
|
+
value: `${current.trimEnd()}${suffix}[features]
|
|
8132
|
+
codex_hooks = true
|
|
8133
|
+
`
|
|
8134
|
+
};
|
|
8135
|
+
}
|
|
8136
|
+
function inspectRuntimeHooks(options = {}) {
|
|
8137
|
+
const paths = defaultPaths(options);
|
|
8138
|
+
const codexConfigRaw = readTextIfExists(paths.codexConfigPath);
|
|
8139
|
+
const codexHooksRaw = readTextIfExists(paths.codexHooksPath);
|
|
8140
|
+
const claudeSettingsRaw = readTextIfExists(paths.claudeSettingsPath);
|
|
8141
|
+
return {
|
|
8142
|
+
paths,
|
|
8143
|
+
installed: {
|
|
8144
|
+
claudeCode: hasOrgxHook(claudeSettingsRaw),
|
|
8145
|
+
codex: hasOrgxHook(codexHooksRaw),
|
|
8146
|
+
hookScript: existsSync6(paths.hookScriptPath)
|
|
8147
|
+
},
|
|
8148
|
+
codex: {
|
|
8149
|
+
configExists: Boolean(codexConfigRaw),
|
|
8150
|
+
hooksEnabled: codexHooksEnabled(codexConfigRaw),
|
|
8151
|
+
hasNotify: codexHasNotify(codexConfigRaw),
|
|
8152
|
+
notifyPreserved: !codexHasNotify(codexConfigRaw) || !hasOrgxHook(codexConfigRaw)
|
|
8153
|
+
},
|
|
8154
|
+
outboxEvents: countJsonlLines(paths.outboxPath)
|
|
8155
|
+
};
|
|
8156
|
+
}
|
|
8157
|
+
function installRuntimeHooks(targets, options = {}) {
|
|
8158
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
8159
|
+
const paths = defaultPaths(options);
|
|
8160
|
+
const backups = [];
|
|
8161
|
+
const changed = {
|
|
8162
|
+
claudeCode: false,
|
|
8163
|
+
codex: false,
|
|
8164
|
+
codexConfig: false,
|
|
8165
|
+
hookScript: false
|
|
8166
|
+
};
|
|
8167
|
+
mkdirSync3(dirname4(paths.hookScriptPath), { recursive: true, mode: 448 });
|
|
8168
|
+
const scriptContent = buildRuntimeHookScriptContent();
|
|
8169
|
+
if (readTextIfExists(paths.hookScriptPath) !== scriptContent) {
|
|
8170
|
+
const backup = backupExisting(paths.hookScriptPath, now);
|
|
8171
|
+
if (backup) backups.push(backup);
|
|
8172
|
+
writeTextFile(paths.hookScriptPath, scriptContent, { mode: 448 });
|
|
8173
|
+
changed.hookScript = true;
|
|
8174
|
+
}
|
|
8175
|
+
if (targets.includes("codex")) {
|
|
8176
|
+
const rawConfig = readTextIfExists(paths.codexConfigPath);
|
|
8177
|
+
const nextConfig = ensureCodexHooksFeature(rawConfig);
|
|
8178
|
+
if (nextConfig.changed) {
|
|
8179
|
+
const backup = backupExisting(paths.codexConfigPath, now);
|
|
8180
|
+
if (backup) backups.push(backup);
|
|
8181
|
+
writeTextFile(paths.codexConfigPath, nextConfig.value, { mode: 384 });
|
|
8182
|
+
changed.codexConfig = true;
|
|
8183
|
+
}
|
|
8184
|
+
const rawHooks = readTextIfExists(paths.codexHooksPath);
|
|
8185
|
+
const nextHooks = mergeCodexHooks(rawHooks, paths);
|
|
8186
|
+
if (nextHooks.changed) {
|
|
8187
|
+
const backup = backupExisting(paths.codexHooksPath, now);
|
|
8188
|
+
if (backup) backups.push(backup);
|
|
8189
|
+
writeJsonFile(paths.codexHooksPath, nextHooks.value, { mode: 384 });
|
|
8190
|
+
changed.codex = true;
|
|
8191
|
+
}
|
|
8192
|
+
}
|
|
8193
|
+
if (targets.includes("claude-code")) {
|
|
8194
|
+
const rawSettings = readTextIfExists(paths.claudeSettingsPath);
|
|
8195
|
+
const nextSettings = mergeClaudeHooks(rawSettings, paths);
|
|
8196
|
+
if (nextSettings.changed) {
|
|
8197
|
+
const backup = backupExisting(paths.claudeSettingsPath, now);
|
|
8198
|
+
if (backup) backups.push(backup);
|
|
8199
|
+
writeJsonFile(paths.claudeSettingsPath, nextSettings.value, { mode: 384 });
|
|
8200
|
+
changed.claudeCode = true;
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
8203
|
+
return {
|
|
8204
|
+
...inspectRuntimeHooks(options),
|
|
8205
|
+
changed,
|
|
8206
|
+
backups
|
|
8207
|
+
};
|
|
8208
|
+
}
|
|
8209
|
+
function parseRuntimeHookTargets(value) {
|
|
8210
|
+
if (!value?.trim()) return ["codex", "claude-code"];
|
|
8211
|
+
const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
|
|
8212
|
+
const expanded = requested.includes("all") ? ["codex", "claude-code"] : requested;
|
|
8213
|
+
const normalized = expanded.map((target) => {
|
|
8214
|
+
if (target === "claude" || target === "claude_code") return "claude-code";
|
|
8215
|
+
return target;
|
|
8216
|
+
});
|
|
8217
|
+
const invalid = normalized.filter((target) => target !== "codex" && target !== "claude-code");
|
|
8218
|
+
if (invalid.length > 0) {
|
|
8219
|
+
throw new Error(`Unsupported hook target: ${invalid.join(", ")}. Use codex, claude-code, or all.`);
|
|
8220
|
+
}
|
|
8221
|
+
return [...new Set(normalized)];
|
|
8222
|
+
}
|
|
8223
|
+
|
|
6892
8224
|
// src/spinner.ts
|
|
6893
8225
|
import ora from "ora";
|
|
6894
8226
|
import pc2 from "picocolors";
|
|
@@ -6992,12 +8324,31 @@ function printPluginMutationReport(report) {
|
|
|
6992
8324
|
function formatScoreLine(scores) {
|
|
6993
8325
|
return Object.entries(scores).map(([dimension, score]) => `${dimension}=${score}`).join(" ");
|
|
6994
8326
|
}
|
|
8327
|
+
function formatWorkGraphScoreLine(score) {
|
|
8328
|
+
return [
|
|
8329
|
+
`overall=${score.overall}`,
|
|
8330
|
+
`value=${score.value_potential}`,
|
|
8331
|
+
`evidence=${score.evidence_quality}`,
|
|
8332
|
+
`urgency=${score.urgency}`,
|
|
8333
|
+
`owner=${score.owner_clarity}`,
|
|
8334
|
+
`automation=${score.automation_potential}`,
|
|
8335
|
+
`orgx_fit=${score.orgx_fit}`
|
|
8336
|
+
].join(" ");
|
|
8337
|
+
}
|
|
8338
|
+
function printRuntimeHookInspection(report) {
|
|
8339
|
+
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)}`);
|
|
8340
|
+
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)}`);
|
|
8341
|
+
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)}`);
|
|
8342
|
+
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")}`);
|
|
8343
|
+
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)}`);
|
|
8344
|
+
console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
|
|
8345
|
+
}
|
|
6995
8346
|
function readAuditInput(options, interactive) {
|
|
6996
8347
|
if (options.input?.trim()) {
|
|
6997
|
-
return
|
|
8348
|
+
return readFileSync5(resolve(options.input.trim()), "utf8");
|
|
6998
8349
|
}
|
|
6999
8350
|
if (!process.stdin.isTTY) {
|
|
7000
|
-
return
|
|
8351
|
+
return readFileSync5(0, "utf8");
|
|
7001
8352
|
}
|
|
7002
8353
|
if (!interactive) {
|
|
7003
8354
|
throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
|
|
@@ -7171,6 +8522,70 @@ async function runAuditCommand(options) {
|
|
|
7171
8522
|
console.log(` ${ICON.ok} ${pc3.green("follow-up ")} ${pc3.bold(followUp.title)} ${pc3.dim(followUp.id)}`);
|
|
7172
8523
|
}
|
|
7173
8524
|
}
|
|
8525
|
+
async function runWorkGraphCommand(options, defaults = {}) {
|
|
8526
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
8527
|
+
const commandOptions = {
|
|
8528
|
+
...options,
|
|
8529
|
+
...options.from?.trim() ? {} : defaults.from ? { from: defaults.from } : {}
|
|
8530
|
+
};
|
|
8531
|
+
const auditImports = await readAuditImports(commandOptions, interactive);
|
|
8532
|
+
const workspace = await resolveAuditWorkspace(commandOptions);
|
|
8533
|
+
const report = buildSessionReconciliationReport({
|
|
8534
|
+
connectedSources: [
|
|
8535
|
+
...auditImports.connectedSources,
|
|
8536
|
+
...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
|
|
8537
|
+
],
|
|
8538
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8539
|
+
imports: auditImports.imports,
|
|
8540
|
+
missingSources: [
|
|
8541
|
+
...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
|
|
8542
|
+
...auditImports.missingSources
|
|
8543
|
+
],
|
|
8544
|
+
workspace
|
|
8545
|
+
});
|
|
8546
|
+
const markdown = renderWorkGraphMarkdown(report);
|
|
8547
|
+
const outputDir = resolve(commandOptions.outputDir?.trim() || ".orgx/work-graph");
|
|
8548
|
+
const timestamp = report.generated_at.replace(/[:.]/g, "-");
|
|
8549
|
+
const jsonPath = resolve(outputDir, `work-graph-report-${timestamp}.json`);
|
|
8550
|
+
const markdownPath = resolve(outputDir, `work-graph-report-${timestamp}.md`);
|
|
8551
|
+
writeJsonFile(jsonPath, report);
|
|
8552
|
+
writeTextFile(markdownPath, markdown);
|
|
8553
|
+
if (commandOptions.json) {
|
|
8554
|
+
console.log(JSON.stringify({
|
|
8555
|
+
jsonPath,
|
|
8556
|
+
markdownPath,
|
|
8557
|
+
reportId: report.report_id,
|
|
8558
|
+
workGraphFingerprint: report.work_graph_fingerprint,
|
|
8559
|
+
hydrationKey: report.signup_hydration.hydration_key,
|
|
8560
|
+
finalState: report.final_state,
|
|
8561
|
+
opportunityScore: report.opportunity_score,
|
|
8562
|
+
missedOrchestration: report.missed_orchestration_opportunities.length,
|
|
8563
|
+
kickoffCount: report.initiative_kickoffs.length,
|
|
8564
|
+
trailCount: report.trails.length,
|
|
8565
|
+
recurringPatternCount: report.recurring_patterns.length,
|
|
8566
|
+
topTrail: report.mirror.primary_trail_id ?? null,
|
|
8567
|
+
mirrorHeadline: report.mirror.headline
|
|
8568
|
+
}, null, 2));
|
|
8569
|
+
return;
|
|
8570
|
+
}
|
|
8571
|
+
console.log(` ${ICON.ok} ${pc3.green("work graph ")} ${pc3.dim(markdownPath)}`);
|
|
8572
|
+
console.log(` ${ICON.ok} ${pc3.green("report id ")} ${pc3.dim(report.report_id)}`);
|
|
8573
|
+
console.log(` ${ICON.ok} ${pc3.green("fingerprint ")} ${pc3.dim(report.work_graph_fingerprint)}`);
|
|
8574
|
+
console.log(` ${ICON.ok} ${pc3.green("state ")} ${pc3.dim(report.final_state)}`);
|
|
8575
|
+
console.log(` ${ICON.ok} ${pc3.green("score ")} ${pc3.dim(formatWorkGraphScoreLine(report.opportunity_score))}`);
|
|
8576
|
+
const missed = report.missed_orchestration_opportunities.length;
|
|
8577
|
+
const missedColor = missed > 0 ? pc3.yellow : pc3.green;
|
|
8578
|
+
console.log(` ${missed > 0 ? ICON.warn : ICON.ok} ${missedColor("missed ")} ${pc3.dim(`${missed} orchestration opportunit${missed === 1 ? "y" : "ies"}`)}`);
|
|
8579
|
+
console.log(` ${ICON.ok} ${pc3.green("trails ")} ${pc3.dim(`${report.trails.length} trail${report.trails.length === 1 ? "" : "s"} \xB7 ${report.recurring_patterns.length} recurring pattern${report.recurring_patterns.length === 1 ? "" : "s"}`)}`);
|
|
8580
|
+
console.log(` ${ICON.skip} ${pc3.bold("mirror ")} ${report.mirror.headline}`);
|
|
8581
|
+
for (const metric of report.tension_metrics.slice(0, 4)) {
|
|
8582
|
+
const tone = metric.tone === "danger" ? pc3.red : metric.tone === "warning" ? pc3.yellow : metric.tone === "good" ? pc3.green : pc3.dim;
|
|
8583
|
+
console.log(` ${ICON.skip} ${tone(`${metric.value} ${metric.label}`)} ${pc3.dim(metric.explanation)}`);
|
|
8584
|
+
}
|
|
8585
|
+
for (const kickoff of report.initiative_kickoffs) {
|
|
8586
|
+
console.log(` ${ICON.skip} ${pc3.bold(kickoff.priority.padEnd(3))} ${kickoff.title}`);
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
7174
8589
|
async function checkPluginStatusesCompact() {
|
|
7175
8590
|
const spinner = createOrgxSpinner("Checking OrgX companion plugin status");
|
|
7176
8591
|
spinner.start();
|
|
@@ -8129,7 +9544,7 @@ function printDoctorReport(report, assessment) {
|
|
|
8129
9544
|
async function main() {
|
|
8130
9545
|
const program = new Command();
|
|
8131
9546
|
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.
|
|
9547
|
+
const pkgVersion = true ? "0.1.28" : void 0;
|
|
8133
9548
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
8134
9549
|
program.hook("preAction", () => {
|
|
8135
9550
|
console.log(renderBanner(pkgVersion));
|
|
@@ -8828,6 +10243,66 @@ async function main() {
|
|
|
8828
10243
|
});
|
|
8829
10244
|
await runAuditCommand(options);
|
|
8830
10245
|
});
|
|
10246
|
+
const workGraph = program.command("work-graph").description("Build a redacted OrgX Work Graph report from AI-client, Slack, MCP, or manual context.");
|
|
10247
|
+
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) => {
|
|
10248
|
+
await safeTrackWizardTelemetry("work_graph_preview_started", {
|
|
10249
|
+
command: "work-graph preview",
|
|
10250
|
+
from: options.from ?? "manual"
|
|
10251
|
+
});
|
|
10252
|
+
await runWorkGraphCommand(options);
|
|
10253
|
+
});
|
|
10254
|
+
workGraph.command("profile").description("Build a local OrgX Profile with Work Graph Trails, Mirror, tensions, and launch recommendations.").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", "5").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 profiles").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) => {
|
|
10255
|
+
await safeTrackWizardTelemetry("work_graph_profile_started", {
|
|
10256
|
+
command: "work-graph profile",
|
|
10257
|
+
from: options.from ?? "manual"
|
|
10258
|
+
});
|
|
10259
|
+
await runWorkGraphCommand(options);
|
|
10260
|
+
});
|
|
10261
|
+
const sessions = program.command("sessions").description("Inspect and reconcile local AI sessions into OrgX-ready Work Graph reports.");
|
|
10262
|
+
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) => {
|
|
10263
|
+
await safeTrackWizardTelemetry("sessions_reconcile_started", {
|
|
10264
|
+
command: "sessions reconcile",
|
|
10265
|
+
from: options.from ?? "all"
|
|
10266
|
+
});
|
|
10267
|
+
await runWorkGraphCommand(options, { from: "all" });
|
|
10268
|
+
});
|
|
10269
|
+
const hooks = program.command("hooks").description("Inspect or install passive OrgX runtime hooks for local agent clients.");
|
|
10270
|
+
hooks.command("doctor").description("Show Codex and Claude Code runtime hook wiring status.").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
10271
|
+
const report = inspectRuntimeHooks();
|
|
10272
|
+
await safeTrackWizardTelemetry("hooks_doctor_ran", {
|
|
10273
|
+
command: "hooks doctor",
|
|
10274
|
+
codex_installed: report.installed.codex,
|
|
10275
|
+
claude_code_installed: report.installed.claudeCode,
|
|
10276
|
+
hook_script_installed: report.installed.hookScript
|
|
10277
|
+
});
|
|
10278
|
+
if (options.json) {
|
|
10279
|
+
console.log(JSON.stringify(report, null, 2));
|
|
10280
|
+
return;
|
|
10281
|
+
}
|
|
10282
|
+
printRuntimeHookInspection(report);
|
|
10283
|
+
});
|
|
10284
|
+
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) => {
|
|
10285
|
+
const targets = parseRuntimeHookTargets(options.targets);
|
|
10286
|
+
const result = installRuntimeHooks(targets);
|
|
10287
|
+
await safeTrackWizardTelemetry("hooks_install_ran", {
|
|
10288
|
+
command: "hooks install",
|
|
10289
|
+
targets: targets.join(","),
|
|
10290
|
+
codex_changed: result.changed.codex,
|
|
10291
|
+
claude_code_changed: result.changed.claudeCode
|
|
10292
|
+
});
|
|
10293
|
+
if (options.json) {
|
|
10294
|
+
console.log(JSON.stringify(result, null, 2));
|
|
10295
|
+
return;
|
|
10296
|
+
}
|
|
10297
|
+
printRuntimeHookInspection(result);
|
|
10298
|
+
if (result.backups.length > 0) {
|
|
10299
|
+
console.log("");
|
|
10300
|
+
console.log(pc3.dim(" backups"));
|
|
10301
|
+
for (const backup of result.backups) {
|
|
10302
|
+
console.log(` ${ICON.skip} ${pc3.dim(backup)}`);
|
|
10303
|
+
}
|
|
10304
|
+
}
|
|
10305
|
+
});
|
|
8831
10306
|
program.command("doctor").description("Verify local OrgX surface config and optional remote setup status.").action(async () => {
|
|
8832
10307
|
const spinner = createOrgxSpinner("Running OrgX health check");
|
|
8833
10308
|
spinner.start();
|