@odla-ai/cli 0.21.0 → 0.22.0

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/bin.cjs CHANGED
@@ -7799,13 +7799,154 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
7799
7799
  }
7800
7800
  }
7801
7801
 
7802
+ // src/record.ts
7803
+ var import_node_fs13 = require("fs");
7804
+ var import_node_process8 = __toESM(require("process"), 1);
7805
+
7806
+ // src/surface.ts
7807
+ var PM_ACTIONS = {
7808
+ list: {},
7809
+ add: {},
7810
+ create: {},
7811
+ get: {},
7812
+ set: {},
7813
+ update: {},
7814
+ status: {},
7815
+ move: {},
7816
+ done: {},
7817
+ comment: {},
7818
+ comments: {},
7819
+ rm: {},
7820
+ delete: {}
7821
+ };
7822
+ var PM_ENTITIES = Object.fromEntries(
7823
+ ["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
7824
+ );
7825
+ var COMMAND_SURFACE = {
7826
+ admin: {
7827
+ ai: {
7828
+ show: {},
7829
+ set: {},
7830
+ credentials: {},
7831
+ models: {},
7832
+ usage: {},
7833
+ audit: {},
7834
+ credential: { set: {} }
7835
+ }
7836
+ },
7837
+ app: {
7838
+ archive: {},
7839
+ restore: {},
7840
+ export: {},
7841
+ import: {},
7842
+ rename: {},
7843
+ "refresh-sandbox": {},
7844
+ "go-live": {},
7845
+ promote: {},
7846
+ owners: { list: {}, add: {}, remove: {} }
7847
+ },
7848
+ calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
7849
+ capabilities: {},
7850
+ code: { connect: {} },
7851
+ doctor: {},
7852
+ help: {},
7853
+ init: {},
7854
+ pm: PM_ENTITIES,
7855
+ provision: {},
7856
+ runbook: {
7857
+ ask: {},
7858
+ search: {},
7859
+ impact: {},
7860
+ list: {},
7861
+ get: {},
7862
+ cat: {},
7863
+ new: {},
7864
+ edit: {},
7865
+ comment: {},
7866
+ import: {},
7867
+ visibility: {},
7868
+ publish: {},
7869
+ archive: {},
7870
+ history: {},
7871
+ revert: {},
7872
+ rm: {},
7873
+ lint: {}
7874
+ },
7875
+ secrets: { push: {}, set: {}, "set-clerk-key": {} },
7876
+ security: {
7877
+ plan: {},
7878
+ sources: {},
7879
+ run: {},
7880
+ status: {},
7881
+ report: {},
7882
+ github: { connect: {}, disconnect: {} }
7883
+ },
7884
+ setup: {},
7885
+ skill: { install: {} },
7886
+ smoke: {},
7887
+ version: {},
7888
+ whoami: {}
7889
+ };
7890
+ function acceptedAfter(path) {
7891
+ let node = COMMAND_SURFACE;
7892
+ for (const word of path) {
7893
+ node = node?.[word];
7894
+ if (!node) return [];
7895
+ }
7896
+ return Object.keys(node).sort();
7897
+ }
7898
+ function validateInvocation(words2) {
7899
+ let node = COMMAND_SURFACE;
7900
+ const walked = [];
7901
+ for (const word of words2) {
7902
+ if (Object.keys(node).length === 0) return null;
7903
+ const next = node[word];
7904
+ if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
7905
+ walked.push(word);
7906
+ node = next;
7907
+ }
7908
+ return null;
7909
+ }
7910
+ function describeProblem(problem) {
7911
+ const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
7912
+ return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
7913
+ }
7914
+ function invocationPath(words2) {
7915
+ let node = COMMAND_SURFACE;
7916
+ const path = [];
7917
+ for (const word of words2) {
7918
+ const next = node[word];
7919
+ if (!next) break;
7920
+ path.push(word);
7921
+ node = next;
7922
+ if (Object.keys(node).length === 0) break;
7923
+ }
7924
+ return path;
7925
+ }
7926
+
7927
+ // src/record.ts
7928
+ function recordInvocation(parsed) {
7929
+ const file = import_node_process8.default.env.ODLA_CLI_RECORD;
7930
+ if (!file) return;
7931
+ try {
7932
+ const entry = {
7933
+ path: invocationPath(parsed.positionals),
7934
+ options: Object.keys(parsed.options).sort()
7935
+ };
7936
+ if (!entry.path.length) return;
7937
+ (0, import_node_fs13.appendFileSync)(file, `${JSON.stringify(entry)}
7938
+ `);
7939
+ } catch {
7940
+ }
7941
+ }
7942
+
7802
7943
  // src/runbook-command.ts
7803
- var import_node_fs17 = require("fs");
7944
+ var import_node_fs18 = require("fs");
7804
7945
  var import_node_path14 = require("path");
7805
- var import_node_process9 = __toESM(require("process"), 1);
7946
+ var import_node_process10 = __toESM(require("process"), 1);
7806
7947
 
7807
7948
  // src/runbook-actions.ts
7808
- var import_node_fs13 = require("fs");
7949
+ var import_node_fs14 = require("fs");
7809
7950
 
7810
7951
  // src/runbook-requires.ts
7811
7952
  var SPEC = /^(@?[\w./-]+?)@(\d+\.\d+\.\d+(?:[\w.-]*)?)$/;
@@ -7890,7 +8031,7 @@ async function bySlug(ctx, slug) {
7890
8031
  function readBody(file, inline) {
7891
8032
  if (inline !== void 0) return inline;
7892
8033
  if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
7893
- return (0, import_node_fs13.readFileSync)(file === "-" ? 0 : file, "utf8");
8034
+ return (0, import_node_fs14.readFileSync)(file === "-" ? 0 : file, "utf8");
7894
8035
  }
7895
8036
  var stamp = (ms) => ms ? new Date(ms).toISOString().slice(0, 16).replace("T", " ") : "";
7896
8037
  async function runbookList(ctx, all, query) {
@@ -7977,7 +8118,7 @@ async function runbookRemove(ctx, slug) {
7977
8118
  }
7978
8119
 
7979
8120
  // src/runbook-import.ts
7980
- var import_node_fs14 = require("fs");
8121
+ var import_node_fs15 = require("fs");
7981
8122
  var import_node_path11 = require("path");
7982
8123
  function parseRunbook(text, slug) {
7983
8124
  let rest = text;
@@ -8003,12 +8144,12 @@ function parseRunbook(text, slug) {
8003
8144
  };
8004
8145
  }
8005
8146
  function readRunbookDir(dir) {
8006
- if (!(0, import_node_fs14.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
8007
- const files = (0, import_node_fs14.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
8147
+ if (!(0, import_node_fs15.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
8148
+ const files = (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
8008
8149
  if (!files.length) throw new Error(`no .md files in ${dir}`);
8009
8150
  return files.map((file) => {
8010
8151
  const slug = (0, import_node_path11.basename)(file, ".md");
8011
- const parsed = parseRunbook((0, import_node_fs14.readFileSync)((0, import_node_path11.join)(dir, file), "utf8"), slug);
8152
+ const parsed = parseRunbook((0, import_node_fs15.readFileSync)((0, import_node_path11.join)(dir, file), "utf8"), slug);
8012
8153
  return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
8013
8154
  });
8014
8155
  }
@@ -8080,7 +8221,7 @@ async function upsert(ctx, r, visibility) {
8080
8221
 
8081
8222
  // src/runbook-impact.ts
8082
8223
  var import_node_child_process7 = require("child_process");
8083
- var import_node_fs15 = require("fs");
8224
+ var import_node_fs16 = require("fs");
8084
8225
  var import_node_path12 = require("path");
8085
8226
 
8086
8227
  // src/runbook-impact-scan.ts
@@ -8251,9 +8392,9 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
8251
8392
  function manifestLabeller(root) {
8252
8393
  return (workspace) => {
8253
8394
  const manifest = (0, import_node_path12.join)(root, workspace, "package.json");
8254
- if (!(0, import_node_fs15.existsSync)(manifest)) return void 0;
8395
+ if (!(0, import_node_fs16.existsSync)(manifest)) return void 0;
8255
8396
  try {
8256
- const name = JSON.parse((0, import_node_fs15.readFileSync)(manifest, "utf8")).name;
8397
+ const name = JSON.parse((0, import_node_fs16.readFileSync)(manifest, "utf8")).name;
8257
8398
  return typeof name === "string" ? name : void 0;
8258
8399
  } catch {
8259
8400
  return void 0;
@@ -8320,7 +8461,7 @@ function report2(ctx, impacts) {
8320
8461
  async function runbookImpact(ctx, options, deps = {}) {
8321
8462
  const cwd = deps.cwd ?? process.cwd();
8322
8463
  const runGit = deps.runGit ?? gitRunner(cwd);
8323
- const read2 = deps.readRepoFile ?? ((path) => (0, import_node_fs15.readFileSync)((0, import_node_path12.join)(cwd, path), "utf8"));
8464
+ const read2 = deps.readRepoFile ?? ((path) => (0, import_node_fs16.readFileSync)((0, import_node_path12.join)(cwd, path), "utf8"));
8324
8465
  const surfaces = changedSurfaces(collectDiff(runGit, options.base, read2), manifestLabeller(cwd));
8325
8466
  if (!surfaces.length) {
8326
8467
  return ctx.out.log(
@@ -8332,115 +8473,6 @@ async function runbookImpact(ctx, options, deps = {}) {
8332
8473
  report2(ctx, impacts);
8333
8474
  }
8334
8475
 
8335
- // src/surface.ts
8336
- var PM_ACTIONS = {
8337
- list: {},
8338
- add: {},
8339
- create: {},
8340
- get: {},
8341
- set: {},
8342
- update: {},
8343
- status: {},
8344
- move: {},
8345
- done: {},
8346
- comment: {},
8347
- comments: {},
8348
- rm: {},
8349
- delete: {}
8350
- };
8351
- var PM_ENTITIES = Object.fromEntries(
8352
- ["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
8353
- );
8354
- var COMMAND_SURFACE = {
8355
- admin: {
8356
- ai: {
8357
- show: {},
8358
- set: {},
8359
- credentials: {},
8360
- models: {},
8361
- usage: {},
8362
- audit: {},
8363
- credential: { set: {} }
8364
- }
8365
- },
8366
- app: {
8367
- archive: {},
8368
- restore: {},
8369
- export: {},
8370
- import: {},
8371
- rename: {},
8372
- "refresh-sandbox": {},
8373
- "go-live": {},
8374
- promote: {},
8375
- owners: { list: {}, add: {}, remove: {} }
8376
- },
8377
- calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
8378
- capabilities: {},
8379
- code: { connect: {} },
8380
- doctor: {},
8381
- help: {},
8382
- init: {},
8383
- pm: PM_ENTITIES,
8384
- provision: {},
8385
- runbook: {
8386
- ask: {},
8387
- search: {},
8388
- impact: {},
8389
- list: {},
8390
- get: {},
8391
- cat: {},
8392
- new: {},
8393
- edit: {},
8394
- comment: {},
8395
- import: {},
8396
- visibility: {},
8397
- publish: {},
8398
- archive: {},
8399
- history: {},
8400
- revert: {},
8401
- rm: {},
8402
- lint: {}
8403
- },
8404
- secrets: { push: {}, set: {}, "set-clerk-key": {} },
8405
- security: {
8406
- plan: {},
8407
- sources: {},
8408
- run: {},
8409
- status: {},
8410
- report: {},
8411
- github: { connect: {}, disconnect: {} }
8412
- },
8413
- setup: {},
8414
- skill: { install: {} },
8415
- smoke: {},
8416
- version: {},
8417
- whoami: {}
8418
- };
8419
- function acceptedAfter(path) {
8420
- let node = COMMAND_SURFACE;
8421
- for (const word of path) {
8422
- node = node?.[word];
8423
- if (!node) return [];
8424
- }
8425
- return Object.keys(node).sort();
8426
- }
8427
- function validateInvocation(words2) {
8428
- let node = COMMAND_SURFACE;
8429
- const walked = [];
8430
- for (const word of words2) {
8431
- if (Object.keys(node).length === 0) return null;
8432
- const next = node[word];
8433
- if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
8434
- walked.push(word);
8435
- node = next;
8436
- }
8437
- return null;
8438
- }
8439
- function describeProblem(problem) {
8440
- const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
8441
- return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
8442
- }
8443
-
8444
8476
  // src/runbook-lint.ts
8445
8477
  function invocationsIn(body) {
8446
8478
  const re = /(`|npx[^\S\n]+)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
@@ -8562,12 +8594,12 @@ async function runbookComment(ctx, slug, body) {
8562
8594
 
8563
8595
  // src/runbook-editor.ts
8564
8596
  var import_node_child_process8 = require("child_process");
8565
- var import_node_fs16 = require("fs");
8597
+ var import_node_fs17 = require("fs");
8566
8598
  var import_node_os4 = require("os");
8567
8599
  var import_node_path13 = require("path");
8568
- var import_node_process8 = __toESM(require("process"), 1);
8600
+ var import_node_process9 = __toESM(require("process"), 1);
8569
8601
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
8570
- function resolveEditor(env = import_node_process8.default.env) {
8602
+ function resolveEditor(env = import_node_process9.default.env) {
8571
8603
  for (const name of EDITOR_ENV) {
8572
8604
  const value = env[name];
8573
8605
  if (value && value.trim()) return value.trim();
@@ -8581,8 +8613,8 @@ function defaultRun(command, path) {
8581
8613
  return result.status ?? 0;
8582
8614
  }
8583
8615
  function editText(initial, slug, deps = {}) {
8584
- const env = deps.env ?? import_node_process8.default.env;
8585
- const interactive = deps.interactive ?? (() => Boolean(import_node_process8.default.stdin.isTTY));
8616
+ const env = deps.env ?? import_node_process9.default.env;
8617
+ const interactive = deps.interactive ?? (() => Boolean(import_node_process9.default.stdin.isTTY));
8586
8618
  const editor = resolveEditor(env);
8587
8619
  if (!editor)
8588
8620
  throw new Error(
@@ -8590,16 +8622,16 @@ function editText(initial, slug, deps = {}) {
8590
8622
  );
8591
8623
  if (!interactive())
8592
8624
  throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
8593
- const dir = (0, import_node_fs16.mkdtempSync)((0, import_node_path13.join)((0, import_node_os4.tmpdir)(), "odla-runbook-"));
8625
+ const dir = (0, import_node_fs17.mkdtempSync)((0, import_node_path13.join)((0, import_node_os4.tmpdir)(), "odla-runbook-"));
8594
8626
  const file = (0, import_node_path13.join)(dir, `${slug}.md`);
8595
8627
  try {
8596
- (0, import_node_fs16.writeFileSync)(file, initial, { mode: 384 });
8628
+ (0, import_node_fs17.writeFileSync)(file, initial, { mode: 384 });
8597
8629
  const code = defaultRunOrInjected(deps)(editor, file);
8598
8630
  if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
8599
- const edited = (0, import_node_fs16.readFileSync)(file, "utf8");
8631
+ const edited = (0, import_node_fs17.readFileSync)(file, "utf8");
8600
8632
  return edited === initial ? null : edited;
8601
8633
  } finally {
8602
- (0, import_node_fs16.rmSync)(dir, { recursive: true, force: true });
8634
+ (0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
8603
8635
  }
8604
8636
  }
8605
8637
  var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
@@ -8699,11 +8731,11 @@ var WRITES = /* @__PURE__ */ new Set(["new", "edit", "publish", "archive", "visi
8699
8731
  var CONFIG_FREE = /* @__PURE__ */ new Set(["list", "search", "ask", "get", "cat", "history", "impact", "lint"]);
8700
8732
  async function loadOrDefaultConfig(configPath, action, appId) {
8701
8733
  const projectScoped = appId !== void 0;
8702
- if (!projectScoped && CONFIG_FREE.has(action) && !(0, import_node_fs17.existsSync)((0, import_node_path14.resolve)(configPath))) {
8703
- const rootDir = import_node_process9.default.cwd();
8734
+ if (!projectScoped && CONFIG_FREE.has(action) && !(0, import_node_fs18.existsSync)((0, import_node_path14.resolve)(configPath))) {
8735
+ const rootDir = import_node_process10.default.cwd();
8704
8736
  return {
8705
8737
  configPath,
8706
- platformUrl: import_node_process9.default.env.ODLA_PLATFORM_URL ?? "https://odla.ai",
8738
+ platformUrl: import_node_process10.default.env.ODLA_PLATFORM_URL ?? "https://odla.ai",
8707
8739
  rootDir,
8708
8740
  app: { id: "", name: "" },
8709
8741
  local: { tokenFile: (0, import_node_path14.join)(rootDir, ".odla/dev-token.json") }
@@ -9553,6 +9585,7 @@ function exitCodeFor(err) {
9553
9585
  }
9554
9586
  async function runCli(argv = process.argv.slice(2), dependencies = {}) {
9555
9587
  const parsed = parseArgv(argv);
9588
+ recordInvocation(parsed);
9556
9589
  const command = parsed.positionals[0] ?? "help";
9557
9590
  if (command === "version" || command === "-v" || parsed.options.version === true) {
9558
9591
  assertArgs(parsed, ["version"], 1);