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