@useorgx/wizard 0.1.22 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli.ts
4
4
  import * as clack from "@clack/prompts";
5
5
  import { spawnSync as spawnSync3 } from "child_process";
6
- import { readFileSync as readFileSync3 } from "fs";
6
+ import { readFileSync as readFileSync4 } from "fs";
7
7
  import { hostname } from "os";
8
8
  import { resolve } from "path";
9
9
  import { Command } from "commander";
@@ -81,6 +81,8 @@ var CURSOR_DIR = join(HOME, ".cursor");
81
81
  var CODEX_DIR = join(HOME, ".codex");
82
82
  var OPENCLAW_DIR = join(HOME, ".openclaw");
83
83
  var AGENTS_DIR = join(HOME, ".agents");
84
+ var CLAUDE_PROJECTS_DIR = join(CLAUDE_DIR, "projects");
85
+ var CODEX_SESSIONS_DIR = join(CODEX_DIR, "sessions");
84
86
  var CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, "skills");
85
87
  var CLAUDE_ORGX_SKILL_DIR = join(CLAUDE_SKILLS_DIR, "orgx");
86
88
  var CLAUDE_ORGX_SKILL_PATH = join(CLAUDE_ORGX_SKILL_DIR, "SKILL.md");
@@ -4102,8 +4104,8 @@ function encodeRepoPath2(value) {
4102
4104
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
4103
4105
  }
4104
4106
  function isLikelyRepoFilePath(path) {
4105
- const basename2 = path.split("/").pop() ?? path;
4106
- return basename2.includes(".") && !/^\.[^./]+$/.test(basename2);
4107
+ const basename3 = path.split("/").pop() ?? path;
4108
+ return basename3.includes(".") && !/^\.[^./]+$/.test(basename3);
4107
4109
  }
4108
4110
  function buildContentsUrl2(spec, path) {
4109
4111
  const encodedPath = encodeRepoPath2(path);
@@ -5927,6 +5929,177 @@ async function fetchOnboardingState(auth) {
5927
5929
  }
5928
5930
  }
5929
5931
 
5932
+ // src/lib/ai-session-import.ts
5933
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
5934
+ import { basename as basename2, join as join4, relative as relative2 } from "path";
5935
+ var AI_SESSION_SOURCES = ["codex", "claude"];
5936
+ var DEFAULT_LIMIT_PER_SOURCE = 3;
5937
+ var DEFAULT_SINCE_DAYS = 30;
5938
+ var DEFAULT_MAX_BYTES_PER_FILE = 1e6;
5939
+ 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;
5940
+ function parseAiSessionSources(value) {
5941
+ if (!value?.trim()) return [];
5942
+ const requested = value.split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
5943
+ const expanded = requested.includes("all") ? [...AI_SESSION_SOURCES] : requested;
5944
+ const deduped = [...new Set(expanded)];
5945
+ const invalid = deduped.filter((source) => !AI_SESSION_SOURCES.includes(source));
5946
+ if (invalid.length > 0) {
5947
+ throw new Error(`Unsupported AI-session source: ${invalid.join(", ")}. Use codex, claude, or all.`);
5948
+ }
5949
+ return deduped;
5950
+ }
5951
+ function parseJsonLine(line) {
5952
+ try {
5953
+ return JSON.parse(line);
5954
+ } catch {
5955
+ return null;
5956
+ }
5957
+ }
5958
+ function asText(value) {
5959
+ if (typeof value === "string") return value;
5960
+ if (Array.isArray(value)) {
5961
+ return value.map((item) => {
5962
+ if (typeof item === "string") return item;
5963
+ if (!isRecord(item)) return "";
5964
+ if (typeof item.text === "string") return item.text;
5965
+ if (typeof item.content === "string") return item.content;
5966
+ return "";
5967
+ }).filter(Boolean).join("\n");
5968
+ }
5969
+ if (isRecord(value) && typeof value.text === "string") return value.text;
5970
+ return "";
5971
+ }
5972
+ function extractCodexMessageText(record) {
5973
+ if (!isRecord(record) || record.type !== "response_item" || !isRecord(record.payload)) {
5974
+ return "";
5975
+ }
5976
+ const payload = record.payload;
5977
+ if (payload.type !== "message" || payload.role !== "user" && payload.role !== "assistant") {
5978
+ return "";
5979
+ }
5980
+ return asText(payload.content);
5981
+ }
5982
+ function extractClaudeMessageText(record) {
5983
+ if (!isRecord(record) || record.isMeta === true || !isRecord(record.message)) {
5984
+ return "";
5985
+ }
5986
+ const message = record.message;
5987
+ if (message.role !== "user" && message.role !== "assistant") {
5988
+ return "";
5989
+ }
5990
+ const text2 = asText(message.content);
5991
+ if (/^<local-command-caveat>/i.test(text2.trim())) return "";
5992
+ return text2;
5993
+ }
5994
+ function keepAuditRelevantLines(text2) {
5995
+ return text2.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && AUDIT_RELEVANT_LINE_PATTERN.test(line));
5996
+ }
5997
+ function collectJsonlFiles(root, source) {
5998
+ if (!existsSync5(root)) return [];
5999
+ const files = [];
6000
+ const stack = [root];
6001
+ while (stack.length > 0) {
6002
+ const current = stack.pop();
6003
+ if (!current) continue;
6004
+ let entries;
6005
+ try {
6006
+ entries = readdirSync3(current);
6007
+ } catch {
6008
+ continue;
6009
+ }
6010
+ for (const entry of entries) {
6011
+ const path = join4(current, entry);
6012
+ let stats;
6013
+ try {
6014
+ stats = statSync3(path);
6015
+ } catch {
6016
+ continue;
6017
+ }
6018
+ if (stats.isDirectory()) {
6019
+ stack.push(path);
6020
+ continue;
6021
+ }
6022
+ if (stats.isFile() && path.endsWith(".jsonl")) {
6023
+ files.push({ mtimeMs: stats.mtimeMs, path, source });
6024
+ }
6025
+ }
6026
+ }
6027
+ return files;
6028
+ }
6029
+ function readSessionImport(candidate, root, options) {
6030
+ let stats;
6031
+ try {
6032
+ stats = statSync3(candidate.path);
6033
+ } catch {
6034
+ return null;
6035
+ }
6036
+ if (stats.size > options.maxBytesPerFile) return null;
6037
+ const extractor = candidate.source === "codex" ? extractCodexMessageText : extractClaudeMessageText;
6038
+ const lines = readFileSync3(candidate.path, "utf8").split(/\r?\n/);
6039
+ const relevantLines = [];
6040
+ for (const line of lines) {
6041
+ const record = parseJsonLine(line);
6042
+ const text2 = extractor(record);
6043
+ if (!text2) continue;
6044
+ relevantLines.push(...keepAuditRelevantLines(text2));
6045
+ }
6046
+ const deduped = [...new Set(relevantLines)].slice(0, 80);
6047
+ if (deduped.length === 0) return null;
6048
+ const relativePath = relative2(root, candidate.path);
6049
+ return {
6050
+ sourceId: `${candidate.source}:${basename2(candidate.path, ".jsonl")}`,
6051
+ sourceLabel: `${candidate.source === "codex" ? "Codex" : "Claude"} session ${relativePath}`,
6052
+ text: deduped.join("\n")
6053
+ };
6054
+ }
6055
+ function loadAiSessionImports(options) {
6056
+ const now = options.now ?? /* @__PURE__ */ new Date();
6057
+ const sinceMs = now.getTime() - (options.sinceDays ?? DEFAULT_SINCE_DAYS) * 24 * 60 * 60 * 1e3;
6058
+ const limitPerSource = Math.max(1, options.limitPerSource ?? DEFAULT_LIMIT_PER_SOURCE);
6059
+ const maxBytesPerFile = Math.max(1, options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE);
6060
+ const roots = {
6061
+ claude: options.claudeProjectsDir ?? CLAUDE_PROJECTS_DIR,
6062
+ codex: options.codexSessionsDir ?? CODEX_SESSIONS_DIR
6063
+ };
6064
+ const imports = [];
6065
+ const connectedSources = [];
6066
+ const missingSources = [];
6067
+ let scannedFiles = 0;
6068
+ let skippedFiles = 0;
6069
+ for (const source of options.sources) {
6070
+ const root = roots[source];
6071
+ const candidates = collectJsonlFiles(root, source).filter((file) => file.mtimeMs >= sinceMs).sort((a, b) => b.mtimeMs - a.mtimeMs);
6072
+ if (candidates.length === 0) {
6073
+ missingSources.push(`${source} session store`);
6074
+ continue;
6075
+ }
6076
+ let importedForSource = 0;
6077
+ for (const candidate of candidates) {
6078
+ if (importedForSource >= limitPerSource) break;
6079
+ scannedFiles += 1;
6080
+ const imported = readSessionImport(candidate, root, { maxBytesPerFile });
6081
+ if (!imported) {
6082
+ skippedFiles += 1;
6083
+ continue;
6084
+ }
6085
+ imports.push(imported);
6086
+ importedForSource += 1;
6087
+ }
6088
+ if (importedForSource > 0) {
6089
+ connectedSources.push(`${source === "codex" ? "Codex" : "Claude"} local sessions`);
6090
+ } else {
6091
+ missingSources.push(`${source} audit-relevant session lines`);
6092
+ }
6093
+ }
6094
+ return {
6095
+ connectedSources,
6096
+ imports,
6097
+ missingSources,
6098
+ scannedFiles,
6099
+ skippedFiles
6100
+ };
6101
+ }
6102
+
5930
6103
  // src/lib/self-audit.ts
5931
6104
  import { createHash as createHash3 } from "crypto";
5932
6105
  var SELF_AUDIT_SCHEMA_VERSION = "2026-04-27";
@@ -6529,10 +6702,10 @@ function formatScoreLine(scores) {
6529
6702
  }
6530
6703
  function readAuditInput(options, interactive) {
6531
6704
  if (options.input?.trim()) {
6532
- return readFileSync3(resolve(options.input.trim()), "utf8");
6705
+ return readFileSync4(resolve(options.input.trim()), "utf8");
6533
6706
  }
6534
6707
  if (!process.stdin.isTTY) {
6535
- return readFileSync3(0, "utf8");
6708
+ return readFileSync4(0, "utf8");
6536
6709
  }
6537
6710
  if (!interactive) {
6538
6711
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -6549,6 +6722,53 @@ function readAuditInput(options, interactive) {
6549
6722
  return value;
6550
6723
  });
6551
6724
  }
6725
+ function parsePositiveInteger(value, fallback, label) {
6726
+ if (!value?.trim()) return fallback;
6727
+ const parsed = Number.parseInt(value.trim(), 10);
6728
+ if (!Number.isFinite(parsed) || parsed <= 0) {
6729
+ throw new Error(`${label} must be a positive integer.`);
6730
+ }
6731
+ return parsed;
6732
+ }
6733
+ async function readAuditImports(options, interactive) {
6734
+ const sources = parseAiSessionSources(options.from);
6735
+ const imports = [];
6736
+ const connectedSources = [];
6737
+ const missingSources = [];
6738
+ if (sources.length > 0) {
6739
+ const imported = loadAiSessionImports({
6740
+ ...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve(options.claudeProjectsDir.trim()) } : {},
6741
+ ...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve(options.codexSessionsDir.trim()) } : {},
6742
+ limitPerSource: parsePositiveInteger(options.sessionLimit, 3, "--session-limit"),
6743
+ sinceDays: parsePositiveInteger(options.sessionDays, 30, "--session-days"),
6744
+ sources
6745
+ });
6746
+ imports.push(...imported.imports);
6747
+ connectedSources.push(...imported.connectedSources);
6748
+ missingSources.push(...imported.missingSources);
6749
+ }
6750
+ const shouldReadManualInput = Boolean(options.input?.trim()) || sources.length === 0 || !process.stdin.isTTY;
6751
+ if (shouldReadManualInput) {
6752
+ const text2 = (await readAuditInput(options, interactive)).trim();
6753
+ if (text2) {
6754
+ imports.push({
6755
+ sourceId: "wizard-audit-input",
6756
+ sourceLabel: options.sourceLabel?.trim() || "Wizard audit input",
6757
+ text: text2
6758
+ });
6759
+ connectedSources.push(options.sourceLabel?.trim() || "Manual AI-session import");
6760
+ }
6761
+ }
6762
+ if (imports.length === 0) {
6763
+ const sourceHint = sources.length > 0 ? ` No audit-relevant lines were found in ${sources.join(", ")} sessions.` : "";
6764
+ throw new Error(`Audit input is required.${sourceHint} Pass --input <file>, pipe text, or use --from codex|claude|all.`);
6765
+ }
6766
+ return {
6767
+ connectedSources,
6768
+ imports,
6769
+ missingSources
6770
+ };
6771
+ }
6552
6772
  function requireWriteApproval(options, interactive) {
6553
6773
  const wantsWrite = Boolean(options.createInitiative || options.attachToInitiative || options.writeFollowUp);
6554
6774
  if (!wantsWrite || options.yes || options.dryRun) return true;
@@ -6591,23 +6811,19 @@ async function resolveAuditWorkspace(options) {
6591
6811
  }
6592
6812
  async function runAuditCommand(options) {
6593
6813
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
6594
- const text2 = (await readAuditInput(options, interactive)).trim();
6595
- if (!text2) return;
6814
+ const auditImports = await readAuditImports(options, interactive);
6596
6815
  const workspace = await resolveAuditWorkspace(options);
6597
6816
  const plan = buildSelfAuditPlan({
6598
6817
  connectedSources: [
6599
- options.sourceLabel?.trim() || "Manual AI-session import",
6818
+ ...auditImports.connectedSources,
6600
6819
  ...workspace.id === "local-workspace" ? [] : ["OrgX workspace"]
6601
6820
  ],
6602
6821
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6603
- imports: [
6604
- {
6605
- sourceId: "wizard-audit-input",
6606
- sourceLabel: options.sourceLabel?.trim() || "Wizard audit input",
6607
- text: text2
6608
- }
6822
+ imports: auditImports.imports,
6823
+ missingSources: [
6824
+ ...workspace.id === "local-workspace" ? ["OrgX workspace auth"] : [],
6825
+ ...auditImports.missingSources
6609
6826
  ],
6610
- missingSources: workspace.id === "local-workspace" ? ["OrgX workspace auth", "automatic AI-session import"] : ["automatic AI-session import"],
6611
6827
  workspace
6612
6828
  });
6613
6829
  const markdown = renderSelfAuditMarkdown(plan);
@@ -7580,7 +7796,7 @@ function printDoctorReport(report, assessment) {
7580
7796
  async function main() {
7581
7797
  const program = new Command();
7582
7798
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
7583
- const pkgVersion = true ? "0.1.22" : void 0;
7799
+ const pkgVersion = true ? "0.1.23" : void 0;
7584
7800
  program.version(pkgVersion ?? "unknown", "-V, --version");
7585
7801
  program.hook("preAction", () => {
7586
7802
  console.log(renderBanner(pkgVersion));
@@ -8242,7 +8458,7 @@ async function main() {
8242
8458
  jsonOutput: Boolean(options.json)
8243
8459
  });
8244
8460
  });
8245
- program.command("audit").description("Run the AI-native Founder Loop self-audit from pasted or file-based AI-session context.").option("--input <path>", "AI-session transcript or summary file; stdin is used when piped").option("--source-label <label>", "label for the imported AI-session 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 audits").option("--output-dir <path>", "directory for generated audit JSON and Markdown", ".orgx/audits").option("--attach-to-initiative <id>", "attach the generated audit artifact to an existing OrgX initiative").option("--create-initiative", "create an OrgX initiative from the generated audit plan").option("--write-follow-up", "create one approval-gated OrgX follow-up task from the audit recommendation").option("--workstream-id <id>", "optional workstream id for the generated follow-up task").option("--milestone-id <id>", "optional milestone id for the generated follow-up task").option("--dry-run", "exercise OrgX write paths without sending writes").option("--yes", "approve OrgX write flags in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
8461
+ program.command("audit").description("Run the AI-native Founder Loop self-audit from pasted or file-based AI-session context.").option("--input <path>", "AI-session 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 audit import").option("--claude-projects-dir <path>", "override Claude projects directory for audit import").option("--source-label <label>", "label for the imported AI-session 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 audits").option("--output-dir <path>", "directory for generated audit JSON and Markdown", ".orgx/audits").option("--attach-to-initiative <id>", "attach the generated audit artifact to an existing OrgX initiative").option("--create-initiative", "create an OrgX initiative from the generated audit plan").option("--write-follow-up", "create one approval-gated OrgX follow-up task from the audit recommendation").option("--workstream-id <id>", "optional workstream id for the generated follow-up task").option("--milestone-id <id>", "optional milestone id for the generated follow-up task").option("--dry-run", "exercise OrgX write paths without sending writes").option("--yes", "approve OrgX write flags in non-interactive mode").option("--json", "emit a JSON command summary").action(async (options) => {
8246
8462
  await safeTrackWizardTelemetry("audit_started", {
8247
8463
  attach_to_initiative: Boolean(options.attachToInitiative),
8248
8464
  command: "audit",