@oxagen/cli 2.1.4-705 → 2.1.4-710

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.
Files changed (2) hide show
  1. package/oxagen.mjs +167 -30
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -24941,7 +24941,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
24941
24941
  import { dirname as dirname9, resolve as resolve3 } from "node:path";
24942
24942
  import { fileURLToPath } from "node:url";
24943
24943
  function readVersion() {
24944
- if (true) return "2.1.4-705";
24944
+ if (true) return "2.1.4-710";
24945
24945
  try {
24946
24946
  const here = dirname9(fileURLToPath(import.meta.url));
24947
24947
  const pkg = JSON.parse(
@@ -57199,7 +57199,7 @@ var init_package = __esm({
57199
57199
  "package.json"() {
57200
57200
  package_default = {
57201
57201
  name: "@oxagen/cli",
57202
- version: "2.1.4-705",
57202
+ version: "2.1.4-710",
57203
57203
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
57204
57204
  type: "module",
57205
57205
  main: "./dist/index.js",
@@ -63570,7 +63570,8 @@ var init_names = __esm({
63570
63570
  "tools",
63571
63571
  "agents",
63572
63572
  "policy",
63573
- "workspace"
63573
+ "workspace",
63574
+ "codegraph"
63574
63575
  ];
63575
63576
  TOOL_SEPARATOR = "__";
63576
63577
  BUILTIN_SERVER = "builtin";
@@ -79849,9 +79850,10 @@ async function readImportDocuments(sources, target2, writer) {
79849
79850
  tooLong.push(source.path);
79850
79851
  continue;
79851
79852
  }
79852
- const filename = importFilename(source.path, source.folder);
79853
+ const filename = source.filename ?? importFilename(source.path, source.folder);
79853
79854
  const document = { filename, content };
79854
- if (target2 !== void 0) document.target = target2;
79855
+ const chosen = source.target ?? target2;
79856
+ if (chosen !== void 0) document.target = chosen;
79855
79857
  if (source.source !== void 0) document.source = source.source;
79856
79858
  if (source.files !== void 0 && source.files.length > 0) {
79857
79859
  const files2 = [];
@@ -80042,12 +80044,19 @@ async function runMarkdownImport(documents, run3, writer) {
80042
80044
  return;
80043
80045
  }
80044
80046
  if (!run3.json) writeFileNotes(files2, writer);
80047
+ let conflicts = 0;
80045
80048
  for (const row of records) {
80046
80049
  if (row.action !== null) continue;
80050
+ conflicts += 1;
80047
80051
  writer.writeErr(
80048
80052
  ` Left out ${row.file}:${row.line} (${row.lineage}): it conflicts with ${row.conflict?.lineage ?? "a published record"}.`
80049
80053
  );
80050
80054
  }
80055
+ if (conflicts > 0) {
80056
+ writer.writeErr(
80057
+ ` Left out ${conflicts} conflicting ${conflicts === 1 ? "record" : "records"}. Import ${conflicts === 1 ? "it" : "them"} in the app to choose which statement stays.`
80058
+ );
80059
+ }
80051
80060
  for (const policy of policies) {
80052
80061
  const issue = policy.issues[0];
80053
80062
  if (policy.action !== "skip" || issue === void 0) continue;
@@ -80096,17 +80105,79 @@ __export(steering_import_exports, {
80096
80105
  CATALOGUE_FOLDERS: () => CATALOGUE_FOLDERS,
80097
80106
  STEERING_IMPORT_CAPABILITIES: () => STEERING_IMPORT_CAPABILITIES,
80098
80107
  findMarkdownFiles: () => findMarkdownFiles,
80099
- handleSteeringImport: () => handleSteeringImport
80108
+ handleSteeringImport: () => handleSteeringImport,
80109
+ harnessHome: () => harnessHome
80100
80110
  });
80101
80111
  import { readdir as readdir4, realpath as realpath2, stat as stat4 } from "node:fs/promises";
80112
+ import { homedir as homedir12 } from "node:os";
80102
80113
  import { basename as basename8, extname, join as join51, relative as relative6, sep as sep7 } from "node:path";
80114
+ function envDir2(env, key) {
80115
+ const value = env[key];
80116
+ return value === void 0 || value.trim() === "" ? null : value;
80117
+ }
80118
+ function harnessHome(harness, env = process.env, home = homedir12()) {
80119
+ switch (harness) {
80120
+ case "claude-code": {
80121
+ const config = envDir2(env, "CLAUDE_CONFIG_DIR") ?? join51(home, ".claude");
80122
+ return {
80123
+ harness,
80124
+ folders: [
80125
+ {
80126
+ dir: skillsRoot("claude-code", env, home) ?? join51(config, "skills"),
80127
+ mount: ".claude/skills"
80128
+ },
80129
+ { dir: join51(config, "commands"), mount: ".claude/commands" },
80130
+ { dir: join51(config, "agents"), mount: ".claude/agents" }
80131
+ ],
80132
+ instructions: [
80133
+ { file: join51(config, "CLAUDE.md"), filename: ".claude/CLAUDE.md" }
80134
+ ]
80135
+ };
80136
+ }
80137
+ case "codex": {
80138
+ const codexHome = envDir2(env, "CODEX_HOME") ?? join51(home, ".codex");
80139
+ return {
80140
+ harness,
80141
+ folders: [
80142
+ {
80143
+ dir: skillsRoot("codex", env, home) ?? join51(home, ".agents", "skills"),
80144
+ mount: ".agents/skills"
80145
+ }
80146
+ ],
80147
+ instructions: ["AGENTS.override.md", "AGENTS.md"].map((name) => ({
80148
+ file: join51(codexHome, name),
80149
+ filename: `.codex/${name}`
80150
+ }))
80151
+ };
80152
+ }
80153
+ case "cursor":
80154
+ return {
80155
+ harness,
80156
+ folders: [
80157
+ { dir: join51(home, ".cursor", "skills"), mount: ".cursor/skills" },
80158
+ { dir: join51(home, ".cursor", "commands"), mount: ".cursor/commands" }
80159
+ ],
80160
+ instructions: []
80161
+ };
80162
+ }
80163
+ }
80103
80164
  function byName(a, b3) {
80104
80165
  return a.name < b3.name ? -1 : 1;
80105
80166
  }
80106
80167
  function under(root, path2) {
80107
80168
  return relative6(root, path2).split(sep7).join("/");
80108
80169
  }
80109
- async function findMarkdownFiles(paths) {
80170
+ function isPersonal(rel) {
80171
+ return classifyHarnessFile(rel)?.kind === "personal";
80172
+ }
80173
+ async function isFolder(path2) {
80174
+ try {
80175
+ return (await stat4(path2)).isDirectory();
80176
+ } catch {
80177
+ return false;
80178
+ }
80179
+ }
80180
+ async function findMarkdownFiles(paths, harness) {
80110
80181
  const sources = [];
80111
80182
  const unreadable = [];
80112
80183
  const seen = /* @__PURE__ */ new Set();
@@ -80140,7 +80211,7 @@ async function findMarkdownFiles(paths) {
80140
80211
  }
80141
80212
  return found;
80142
80213
  };
80143
- const walk2 = async (dir, root, folder, inCatalogue) => {
80214
+ const walk2 = async (dir, frame) => {
80144
80215
  let entries2;
80145
80216
  try {
80146
80217
  entries2 = await readdir4(dir, { withFileTypes: true });
@@ -80149,27 +80220,31 @@ async function findMarkdownFiles(paths) {
80149
80220
  return;
80150
80221
  }
80151
80222
  entries2.sort(byName);
80223
+ const { folder } = frame;
80224
+ const named = (rel) => frame.fixedNames ? { filename: rel } : {};
80152
80225
  for (const entry2 of entries2) {
80153
80226
  const path2 = join51(dir, entry2.name);
80154
- const rel = under(root, path2);
80227
+ const rel = frame.relOf(path2);
80155
80228
  if (entry2.isDirectory()) {
80156
80229
  if (entry2.name === "node_modules") continue;
80157
80230
  if (entry2.name.startsWith(".")) {
80158
- if (!inCatalogue && CATALOGUE_FOLDERS.has(entry2.name))
80159
- await walk2(path2, root, folder, true);
80231
+ if (!frame.inCatalogue && CATALOGUE_FOLDERS.has(entry2.name))
80232
+ await walk2(path2, { ...frame, inCatalogue: true });
80160
80233
  continue;
80161
80234
  }
80162
- await walk2(path2, root, folder, inCatalogue);
80235
+ await walk2(path2, frame);
80163
80236
  continue;
80164
80237
  }
80165
80238
  if (!entry2.isFile() && !entry2.isSymbolicLink()) continue;
80166
80239
  const source = markdownImportSourceOf(rel);
80167
- if (inCatalogue) {
80240
+ if (frame.inCatalogue) {
80168
80241
  if (source === null) continue;
80169
80242
  if (source.kind === "skill") {
80243
+ if (entries2.some((e) => e.name === SESSION_MARKER)) continue;
80170
80244
  const skillDir = dir;
80171
80245
  await take(path2, {
80172
80246
  folder,
80247
+ ...named(rel),
80173
80248
  source: { ...source, path: rel },
80174
80249
  files: (await filesUnder(skillDir, skillDir)).map((file) => ({
80175
80250
  path: under(skillDir, file),
@@ -80177,11 +80252,17 @@ async function findMarkdownFiles(paths) {
80177
80252
  }))
80178
80253
  });
80179
80254
  } else {
80180
- await take(path2, { folder, source: { ...source, path: rel } });
80255
+ await take(path2, {
80256
+ folder,
80257
+ ...named(rel),
80258
+ source: { ...source, path: rel }
80259
+ });
80181
80260
  }
80182
80261
  continue;
80183
80262
  }
80184
- if (MARKDOWN_EXTENSIONS.has(extname(entry2.name).toLowerCase())) {
80263
+ if (isPersonal(rel)) {
80264
+ await take(path2, { folder, ...named(rel), target: "memories" });
80265
+ } else if (MARKDOWN_EXTENSIONS.has(extname(entry2.name).toLowerCase())) {
80185
80266
  await take(
80186
80267
  path2,
80187
80268
  source === null ? { folder } : { folder, source: { ...source, path: rel } }
@@ -80192,18 +80273,28 @@ async function findMarkdownFiles(paths) {
80192
80273
  }
80193
80274
  };
80194
80275
  for (const path2 of paths) {
80195
- let isFolder;
80276
+ let folderGiven;
80196
80277
  try {
80197
- isFolder = (await stat4(path2)).isDirectory();
80278
+ folderGiven = (await stat4(path2)).isDirectory();
80198
80279
  } catch {
80199
80280
  unreadable.push(path2);
80200
80281
  continue;
80201
80282
  }
80202
- if (isFolder) {
80203
- const named = CATALOGUE_FOLDERS.has(basename8(path2));
80204
- await walk2(path2, named ? join51(path2, "..") : path2, path2, false);
80283
+ if (folderGiven) {
80284
+ const catalogue = CATALOGUE_FOLDERS.has(basename8(path2));
80285
+ const root = catalogue ? join51(path2, "..") : path2;
80286
+ await walk2(path2, {
80287
+ relOf: (p) => under(root, p),
80288
+ folder: path2,
80289
+ inCatalogue: catalogue,
80290
+ fixedNames: false
80291
+ });
80205
80292
  } else {
80206
80293
  const rel = under(process.cwd(), path2);
80294
+ if (isPersonal(rel.startsWith("..") ? basename8(path2) : rel)) {
80295
+ await take(path2, { target: "memories" });
80296
+ continue;
80297
+ }
80207
80298
  const source = rel.startsWith("..") ? null : markdownImportSourceOf(rel);
80208
80299
  await take(
80209
80300
  path2,
@@ -80211,8 +80302,38 @@ async function findMarkdownFiles(paths) {
80211
80302
  );
80212
80303
  }
80213
80304
  }
80305
+ if (harness !== void 0) {
80306
+ for (const { dir, mount } of harness.folders) {
80307
+ if (!await isFolder(dir)) continue;
80308
+ await walk2(dir, {
80309
+ relOf: (p) => `${mount}/${under(dir, p)}`,
80310
+ folder: dir,
80311
+ inCatalogue: true,
80312
+ fixedNames: true
80313
+ });
80314
+ }
80315
+ for (const { file, filename } of harness.instructions) {
80316
+ let found = false;
80317
+ try {
80318
+ found = (await stat4(file)).isFile();
80319
+ } catch {
80320
+ }
80321
+ if (!found) continue;
80322
+ await take(file, { filename, target: "memories" });
80323
+ break;
80324
+ }
80325
+ }
80214
80326
  return { sources, unreadable };
80215
80327
  }
80328
+ function parseHarness(value, writer) {
80329
+ if (value === void 0) return void 0;
80330
+ const harness = LAPTOP_HARNESSES.find((h) => h === value.toLowerCase());
80331
+ if (harness !== void 0) return harness;
80332
+ failCommand(
80333
+ `Invalid --harness "${value}". Use claude-code, codex, or cursor.`,
80334
+ writer
80335
+ );
80336
+ }
80216
80337
  function parseTarget(value, writer) {
80217
80338
  if (value === void 0) return void 0;
80218
80339
  const target2 = TARGETS.find((t) => t === value.toLowerCase());
@@ -80222,15 +80343,17 @@ function parseTarget(value, writer) {
80222
80343
  writer
80223
80344
  );
80224
80345
  }
80225
- async function handleSteeringImport(paths, opts, writer = stdoutWriter) {
80226
- if (paths.length === 0) {
80346
+ async function handleSteeringImport(paths, opts, writer = stdoutWriter, env = process.env, home = homedir12()) {
80347
+ const harness = parseHarness(opts.harness, writer);
80348
+ if (paths.length === 0 && harness === void 0) {
80227
80349
  failCommand(
80228
- "Nothing to import. Pass Markdown files or folders, such as `oxagen steering import docs`.",
80350
+ "Nothing to import. Pass Markdown files or folders, such as `oxagen steering import docs`, or a harness, such as `oxagen steering import --harness claude-code`.",
80229
80351
  writer
80230
80352
  );
80231
80353
  }
80232
80354
  const target2 = parseTarget(opts.as, writer);
80233
- const walk2 = await findMarkdownFiles(paths);
80355
+ const folders = harness === void 0 ? void 0 : harnessHome(harness, env, home);
80356
+ const walk2 = await findMarkdownFiles(paths, folders);
80234
80357
  if (walk2.unreadable.length > 0) {
80235
80358
  writer.writeErr(
80236
80359
  `Skipped paths that do not exist or cannot be read:
@@ -80238,8 +80361,13 @@ async function handleSteeringImport(paths, opts, writer = stdoutWriter) {
80238
80361
  );
80239
80362
  }
80240
80363
  if (walk2.sources.length === 0) {
80364
+ const where = [
80365
+ ...paths,
80366
+ ...folders?.folders.map((f) => f.dir) ?? [],
80367
+ ...folders?.instructions.map((f) => f.file) ?? []
80368
+ ];
80241
80369
  failCommand(
80242
- `Found no .md, .markdown, .mdx, or .mdc file in ${paths.join(", ")}. Pass a Markdown file, or a folder that holds one.`,
80370
+ folders === void 0 ? `Found no .md, .markdown, .mdx, or .mdc file in ${paths.join(", ")}. Pass a Markdown file, or a folder that holds one.` : `Found no skill, command, subagent file, instruction file, or Markdown file in ${where.join(", ")}.`,
80243
80371
  writer
80244
80372
  );
80245
80373
  }
@@ -80250,11 +80378,12 @@ async function handleSteeringImport(paths, opts, writer = stdoutWriter) {
80250
80378
  writer
80251
80379
  );
80252
80380
  }
80253
- var STEERING_IMPORT_CAPABILITIES, MARKDOWN_EXTENSIONS, CATALOGUE_FOLDERS, TARGETS;
80381
+ var STEERING_IMPORT_CAPABILITIES, MARKDOWN_EXTENSIONS, CATALOGUE_FOLDERS, TARGETS, LAPTOP_HARNESSES;
80254
80382
  var init_steering_import = __esm({
80255
80383
  "src/commands/steering-import.ts"() {
80256
80384
  init_steering_markdown_import_shared();
80257
80385
  init_harness_files();
80386
+ init_skills();
80258
80387
  init_capture_writer();
80259
80388
  init_markdown_import();
80260
80389
  STEERING_IMPORT_CAPABILITIES = [
@@ -80277,6 +80406,11 @@ var init_steering_import = __esm({
80277
80406
  "policies",
80278
80407
  "memories"
80279
80408
  ];
80409
+ LAPTOP_HARNESSES = [
80410
+ "claude-code",
80411
+ "codex",
80412
+ "cursor"
80413
+ ];
80280
80414
  }
80281
80415
  });
80282
80416
 
@@ -82939,7 +83073,7 @@ import {
82939
83073
  realpathSync as realpathSync8,
82940
83074
  writeFileSync as writeFileSync7
82941
83075
  } from "node:fs";
82942
- import { homedir as homedir12, hostname as osHostname3 } from "node:os";
83076
+ import { homedir as homedir13, hostname as osHostname3 } from "node:os";
82943
83077
  import { join as join53, relative as relative7, resolve as resolve19, sep as sep8 } from "node:path";
82944
83078
  function git4(cwd, args) {
82945
83079
  return new Promise((done) => {
@@ -83035,7 +83169,7 @@ function machineId(configDir = getConfigDir()) {
83035
83169
  `, { encoding: "utf8", mode: 384 });
83036
83170
  }
83037
83171
  } catch {
83038
- secret = `fallback:${osHostname3()}:${homedir12()}`;
83172
+ secret = `fallback:${osHostname3()}:${homedir13()}`;
83039
83173
  }
83040
83174
  return createHash17("sha256").update(secret).digest("hex");
83041
83175
  }
@@ -134527,10 +134661,13 @@ function buildProgram() {
134527
134661
  }
134528
134662
  );
134529
134663
  steeringCmd.command("import").argument(
134530
- "<paths...>",
134664
+ "[paths...]",
134531
134665
  "Markdown files, or folders to search for .md, .markdown, .mdx, and .mdc files"
134532
134666
  ).description(
134533
- "Read Markdown files into steering records, Cedar policies, or memories. Previews unless --yes."
134667
+ "Read Markdown files, or your own harness folders, into steering records, Cedar policies, or memories. Previews unless --yes."
134668
+ ).option(
134669
+ "--harness <name>",
134670
+ "claude-code, codex, or cursor. Also read that harness's skills, commands, and subagent files on this machine as workspace records, and its user instruction file, such as ~/.claude/CLAUDE.md, as your memories. Honours CLAUDE_CONFIG_DIR and CODEX_HOME."
134534
134671
  ).option(
134535
134672
  "--as <target>",
134536
134673
  "records, policies, or memories for every file. Without it, each file takes the target its text implies, such as policies for a file with a cedar block."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxagen/cli",
3
- "version": "2.1.4-705",
3
+ "version": "2.1.4-710",
4
4
  "description": "Oxagen governance CLI — spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
5
5
  "type": "module",
6
6
  "bin": {