@oxagen/cli 2.1.4-705 → 2.1.4-707

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