@sinoia/hubdoc-tools 1.10.0 → 1.12.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.
Files changed (3) hide show
  1. package/cli.js +297 -71
  2. package/docs/llm-usage.md +79 -11
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -42589,9 +42589,9 @@ var require_follow_redirects = __commonJS({
42589
42589
  "search",
42590
42590
  "hash"
42591
42591
  ];
42592
- var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
42592
+ var events2 = ["abort", "aborted", "connect", "error", "socket", "timeout"];
42593
42593
  var eventHandlers = /* @__PURE__ */ Object.create(null);
42594
- events.forEach(function(event) {
42594
+ events2.forEach(function(event) {
42595
42595
  eventHandlers[event] = function(arg1, arg2, arg3) {
42596
42596
  this._redirectable.emit(event, arg1, arg2, arg3);
42597
42597
  };
@@ -42805,7 +42805,7 @@ var require_follow_redirects = __commonJS({
42805
42805
  }
42806
42806
  var request = this._currentRequest = nativeProtocol.request(this._options, this._onNativeResponse);
42807
42807
  request._redirectable = this;
42808
- for (var event of events) {
42808
+ for (var event of events2) {
42809
42809
  request.on(event, eventHandlers[event]);
42810
42810
  }
42811
42811
  this._currentUrl = /^\//.test(this._options.path) ? url3.format(this._options) : (
@@ -43020,7 +43020,7 @@ var require_follow_redirects = __commonJS({
43020
43020
  return CustomError;
43021
43021
  }
43022
43022
  function destroyRequest(request, error2) {
43023
- for (var event of events) {
43023
+ for (var event of events2) {
43024
43024
  request.removeListener(event, eventHandlers[event]);
43025
43025
  }
43026
43026
  request.on("error", noop2);
@@ -50043,7 +50043,7 @@ var require_parser = __commonJS({
50043
50043
  "node_modules/xml2js/lib/parser.js"(exports2) {
50044
50044
  (function() {
50045
50045
  "use strict";
50046
- var bom, defaults3, defineProperty, events, isEmpty, processItem, processors, sax, setImmediate2, bind2 = function(fn, me) {
50046
+ var bom, defaults3, defineProperty, events2, isEmpty, processItem, processors, sax, setImmediate2, bind2 = function(fn, me) {
50047
50047
  return function() {
50048
50048
  return fn.apply(me, arguments);
50049
50049
  };
@@ -50060,7 +50060,7 @@ var require_parser = __commonJS({
50060
50060
  return child;
50061
50061
  }, hasProp = {}.hasOwnProperty;
50062
50062
  sax = require_sax();
50063
- events = require("events");
50063
+ events2 = require("events");
50064
50064
  bom = require_bom();
50065
50065
  processors = require_processors();
50066
50066
  setImmediate2 = require("timers").setImmediate;
@@ -50398,7 +50398,7 @@ var require_parser = __commonJS({
50398
50398
  })(this));
50399
50399
  };
50400
50400
  return Parser;
50401
- })(events);
50401
+ })(events2);
50402
50402
  exports2.parseString = function(str, a3, b3) {
50403
50403
  var cb, options, parser;
50404
50404
  if (b3 != null) {
@@ -73349,7 +73349,7 @@ var require_dist_cjs69 = __commonJS({
73349
73349
  return value2;
73350
73350
  };
73351
73351
  };
73352
- var sleep = (seconds) => {
73352
+ var sleep2 = (seconds) => {
73353
73353
  return new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
73354
73354
  };
73355
73355
  var waiterServiceDefaults = {
@@ -73416,7 +73416,7 @@ var require_dist_cjs69 = __commonJS({
73416
73416
  if (Date.now() + delay5 * 1e3 > waitUntil) {
73417
73417
  return { state: exports2.WaiterState.TIMEOUT, observedResponses };
73418
73418
  }
73419
- await sleep(delay5);
73419
+ await sleep2(delay5);
73420
73420
  const { state: state5, reason: reason2 } = await acceptorChecks(client, input);
73421
73421
  if (reason2) {
73422
73422
  const message = createMessageFromResponse(reason2);
@@ -86010,7 +86010,7 @@ var require_dist_cjs72 = __commonJS({
86010
86010
  var abortController = require_dist_cjs71();
86011
86011
  var middlewareEndpoint = require_dist_cjs42();
86012
86012
  var smithyClient = require_dist_cjs20();
86013
- var events = require("events");
86013
+ var events2 = require("events");
86014
86014
  var buffer2 = require("buffer");
86015
86015
  var runtimeConfig = require_runtimeConfig3();
86016
86016
  var stream5 = require("stream");
@@ -86166,7 +86166,7 @@ var require_dist_cjs72 = __commonJS({
86166
86166
  }
86167
86167
  throw new Error("Body Data is unsupported format, expected data to be one of: string | Uint8Array | Buffer | Readable | ReadableStream | Blob;.");
86168
86168
  };
86169
- var Upload2 = class _Upload extends events.EventEmitter {
86169
+ var Upload2 = class _Upload extends events2.EventEmitter {
86170
86170
  static MIN_PART_SIZE = 1024 * 1024 * 5;
86171
86171
  MAX_PARTS = 1e4;
86172
86172
  queueSize = 4;
@@ -86229,7 +86229,7 @@ var require_dist_cjs72 = __commonJS({
86229
86229
  const params = { ...this.params, Body: dataPart.data };
86230
86230
  const clientConfig = this.client.config;
86231
86231
  const requestHandler = clientConfig.requestHandler;
86232
- const eventEmitter = requestHandler instanceof events.EventEmitter ? requestHandler : null;
86232
+ const eventEmitter = requestHandler instanceof events2.EventEmitter ? requestHandler : null;
86233
86233
  const uploadEventListener = (event) => {
86234
86234
  this.bytesUploadedSoFar = event.loaded;
86235
86235
  this.totalBytes = event.total;
@@ -86325,7 +86325,7 @@ var require_dist_cjs72 = __commonJS({
86325
86325
  }
86326
86326
  const partSize = byteLength(dataPart.data) || 0;
86327
86327
  const requestHandler = this.client.config.requestHandler;
86328
- const eventEmitter = requestHandler instanceof events.EventEmitter ? requestHandler : null;
86328
+ const eventEmitter = requestHandler instanceof events2.EventEmitter ? requestHandler : null;
86329
86329
  let lastSeenBytes = 0;
86330
86330
  const uploadEventListener = (event, request) => {
86331
86331
  const requestPartSize = Number(request.query["partNumber"]) || -1;
@@ -95034,7 +95034,7 @@ var require_bignumber = __commonJS({
95034
95034
  "node_modules/bignumber.js/bignumber.js"(exports2, module2) {
95035
95035
  (function(globalObject) {
95036
95036
  "use strict";
95037
- var BigNumber, isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE4 = 1e14, LOG_BASE = 14, MAX_SAFE_INTEGER = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX = 1e9;
95037
+ var BigNumber, isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE6 = 1e14, LOG_BASE = 14, MAX_SAFE_INTEGER = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX = 1e9;
95038
95038
  function clone(configObject) {
95039
95039
  var div, convertBase, parseNumeric, P = BigNumber2.prototype = { constructor: BigNumber2, toString: null, valueOf: null }, ONE = new BigNumber2(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
95040
95040
  prefix: "",
@@ -95286,7 +95286,7 @@ var require_bignumber = __commonJS({
95286
95286
  if (String(c3[0]).length == i3) {
95287
95287
  for (i3 = 0; i3 < c3.length; i3++) {
95288
95288
  n3 = c3[i3];
95289
- if (n3 < 0 || n3 >= BASE4 || n3 !== mathfloor(n3)) break out;
95289
+ if (n3 < 0 || n3 >= BASE6 || n3 !== mathfloor(n3)) break out;
95290
95290
  }
95291
95291
  if (n3 !== 0) return true;
95292
95292
  }
@@ -95503,7 +95503,7 @@ var require_bignumber = __commonJS({
95503
95503
  e3 = x3.e - y2.e;
95504
95504
  s3 = dp + e3 + 1;
95505
95505
  if (!base) {
95506
- base = BASE4;
95506
+ base = BASE6;
95507
95507
  e3 = bitFloor(x3.e / LOG_BASE) - bitFloor(y2.e / LOG_BASE);
95508
95508
  s3 = s3 / LOG_BASE | 0;
95509
95509
  }
@@ -95582,7 +95582,7 @@ var require_bignumber = __commonJS({
95582
95582
  more = rem[0] != null;
95583
95583
  if (!qc[0]) qc.splice(0, 1);
95584
95584
  }
95585
- if (base == BASE4) {
95585
+ if (base == BASE6) {
95586
95586
  for (i3 = 1, s3 = qc[0]; s3 >= 10; s3 /= 10, i3++) ;
95587
95587
  round(q3, dp + (q3.e = i3 + e3 * LOG_BASE - 1) + 1, rm, more);
95588
95588
  } else {
@@ -95741,12 +95741,12 @@ var require_bignumber = __commonJS({
95741
95741
  for (k3 = 1; j3 >= 10; j3 /= 10, k3++) ;
95742
95742
  if (i3 != k3) {
95743
95743
  x3.e++;
95744
- if (xc[0] == BASE4) xc[0] = 1;
95744
+ if (xc[0] == BASE6) xc[0] = 1;
95745
95745
  }
95746
95746
  break;
95747
95747
  } else {
95748
95748
  xc[ni] += k3;
95749
- if (xc[ni] != BASE4) break;
95749
+ if (xc[ni] != BASE6) break;
95750
95750
  xc[ni--] = 0;
95751
95751
  k3 = 1;
95752
95752
  }
@@ -95955,12 +95955,12 @@ var require_bignumber = __commonJS({
95955
95955
  }
95956
95956
  b3 = (j3 = yc.length) - (i3 = xc.length);
95957
95957
  if (b3 > 0) for (; b3--; xc[i3++] = 0) ;
95958
- b3 = BASE4 - 1;
95958
+ b3 = BASE6 - 1;
95959
95959
  for (; j3 > a3; ) {
95960
95960
  if (xc[--j3] < yc[j3]) {
95961
95961
  for (i3 = j3; i3 && !xc[--i3]; xc[i3] = b3) ;
95962
95962
  --xc[i3];
95963
- xc[j3] += BASE4;
95963
+ xc[j3] += BASE6;
95964
95964
  }
95965
95965
  xc[j3] -= yc[j3];
95966
95966
  }
@@ -96022,7 +96022,7 @@ var require_bignumber = __commonJS({
96022
96022
  ycL = i3;
96023
96023
  }
96024
96024
  for (i3 = xcL + ycL, zc = []; i3--; zc.push(0)) ;
96025
- base = BASE4;
96025
+ base = BASE6;
96026
96026
  sqrtBase = SQRT_BASE;
96027
96027
  for (i3 = ycL; --i3 >= 0; ) {
96028
96028
  c3 = 0;
@@ -96088,8 +96088,8 @@ var require_bignumber = __commonJS({
96088
96088
  b3 = a3;
96089
96089
  }
96090
96090
  for (a3 = 0; b3; ) {
96091
- a3 = (xc[--b3] = xc[b3] + yc[b3] + a3) / BASE4 | 0;
96092
- xc[b3] = BASE4 === xc[b3] ? 0 : xc[b3] % BASE4;
96091
+ a3 = (xc[--b3] = xc[b3] + yc[b3] + a3) / BASE6 | 0;
96092
+ xc[b3] = BASE6 === xc[b3] ? 0 : xc[b3] % BASE6;
96093
96093
  }
96094
96094
  if (a3) {
96095
96095
  xc = [a3].concat(xc);
@@ -672699,11 +672699,11 @@ var ListPrompt = class extends Prompt {
672699
672699
  */
672700
672700
  _run(cb) {
672701
672701
  this.done = cb;
672702
- const events = observe(this.rl);
672703
- events.normalizedUpKey.pipe((0, import_rxjs3.takeUntil)(events.line)).forEach(this.onUpKey.bind(this));
672704
- events.normalizedDownKey.pipe((0, import_rxjs3.takeUntil)(events.line)).forEach(this.onDownKey.bind(this));
672705
- events.numberKey.pipe((0, import_rxjs3.takeUntil)(events.line)).forEach(this.onNumberKey.bind(this));
672706
- events.line.pipe(
672702
+ const events2 = observe(this.rl);
672703
+ events2.normalizedUpKey.pipe((0, import_rxjs3.takeUntil)(events2.line)).forEach(this.onUpKey.bind(this));
672704
+ events2.normalizedDownKey.pipe((0, import_rxjs3.takeUntil)(events2.line)).forEach(this.onDownKey.bind(this));
672705
+ events2.numberKey.pipe((0, import_rxjs3.takeUntil)(events2.line)).forEach(this.onNumberKey.bind(this));
672706
+ events2.line.pipe(
672707
672707
  (0, import_rxjs3.take)(1),
672708
672708
  (0, import_rxjs3.map)(this.getCurrentValue.bind(this)),
672709
672709
  (0, import_rxjs3.flatMap)(
@@ -672816,12 +672816,12 @@ var InputPrompt = class extends Prompt {
672816
672816
  */
672817
672817
  _run(cb) {
672818
672818
  this.done = cb;
672819
- const events = observe(this.rl);
672820
- const submit = events.line.pipe((0, import_rxjs4.map)(this.filterInput.bind(this)));
672819
+ const events2 = observe(this.rl);
672820
+ const submit = events2.line.pipe((0, import_rxjs4.map)(this.filterInput.bind(this)));
672821
672821
  const validation = this.handleSubmitEvents(submit);
672822
672822
  validation.success.forEach(this.onEnd.bind(this));
672823
672823
  validation.error.forEach(this.onError.bind(this));
672824
- events.keypress.pipe((0, import_rxjs4.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
672824
+ events2.keypress.pipe((0, import_rxjs4.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
672825
672825
  this.render();
672826
672826
  return this;
672827
672827
  }
@@ -672918,9 +672918,9 @@ var ConfirmPrompt = class extends Prompt {
672918
672918
  */
672919
672919
  _run(cb) {
672920
672920
  this.done = cb;
672921
- const events = observe(this.rl);
672922
- events.keypress.pipe((0, import_rxjs5.takeUntil)(events.line)).forEach(this.onKeypress.bind(this));
672923
- events.line.pipe((0, import_rxjs5.take)(1)).forEach(this.onEnd.bind(this));
672921
+ const events2 = observe(this.rl);
672922
+ events2.keypress.pipe((0, import_rxjs5.takeUntil)(events2.line)).forEach(this.onKeypress.bind(this));
672923
+ events2.line.pipe((0, import_rxjs5.take)(1)).forEach(this.onEnd.bind(this));
672924
672924
  this.render();
672925
672925
  return this;
672926
672926
  }
@@ -673001,14 +673001,14 @@ var RawListPrompt = class extends Prompt {
673001
673001
  */
673002
673002
  _run(cb) {
673003
673003
  this.done = cb;
673004
- const events = observe(this.rl);
673005
- const submit = events.line.pipe((0, import_rxjs6.map)(this.getCurrentValue.bind(this)));
673004
+ const events2 = observe(this.rl);
673005
+ const submit = events2.line.pipe((0, import_rxjs6.map)(this.getCurrentValue.bind(this)));
673006
673006
  const validation = this.handleSubmitEvents(submit);
673007
673007
  validation.success.forEach(this.onEnd.bind(this));
673008
673008
  validation.error.forEach(this.onError.bind(this));
673009
- events.normalizedUpKey.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onUpKey.bind(this));
673010
- events.normalizedDownKey.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onDownKey.bind(this));
673011
- events.keypress.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673009
+ events2.normalizedUpKey.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onUpKey.bind(this));
673010
+ events2.normalizedDownKey.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onDownKey.bind(this));
673011
+ events2.keypress.pipe((0, import_rxjs6.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673012
673012
  this.render();
673013
673013
  return this;
673014
673014
  }
@@ -673145,13 +673145,13 @@ var ExpandPrompt = class extends Prompt {
673145
673145
  */
673146
673146
  _run(cb) {
673147
673147
  this.done = cb;
673148
- const events = observe(this.rl);
673148
+ const events2 = observe(this.rl);
673149
673149
  const validation = this.handleSubmitEvents(
673150
- events.line.pipe((0, import_rxjs7.map)(this.getCurrentValue.bind(this)))
673150
+ events2.line.pipe((0, import_rxjs7.map)(this.getCurrentValue.bind(this)))
673151
673151
  );
673152
673152
  validation.success.forEach(this.onSubmit.bind(this));
673153
673153
  validation.error.forEach(this.onError.bind(this));
673154
- this.keypressObs = events.keypress.pipe((0, import_rxjs7.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673154
+ this.keypressObs = events2.keypress.pipe((0, import_rxjs7.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673155
673155
  this.render();
673156
673156
  return this;
673157
673157
  }
@@ -673338,18 +673338,18 @@ var CheckboxPrompt = class extends Prompt {
673338
673338
  */
673339
673339
  _run(cb) {
673340
673340
  this.done = cb;
673341
- const events = observe(this.rl);
673341
+ const events2 = observe(this.rl);
673342
673342
  const validation = this.handleSubmitEvents(
673343
- events.line.pipe((0, import_rxjs8.map)(this.getCurrentValue.bind(this)))
673343
+ events2.line.pipe((0, import_rxjs8.map)(this.getCurrentValue.bind(this)))
673344
673344
  );
673345
673345
  validation.success.forEach(this.onEnd.bind(this));
673346
673346
  validation.error.forEach(this.onError.bind(this));
673347
- events.normalizedUpKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onUpKey.bind(this));
673348
- events.normalizedDownKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onDownKey.bind(this));
673349
- events.numberKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onNumberKey.bind(this));
673350
- events.spaceKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onSpaceKey.bind(this));
673351
- events.aKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onAllKey.bind(this));
673352
- events.iKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onInverseKey.bind(this));
673347
+ events2.normalizedUpKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onUpKey.bind(this));
673348
+ events2.normalizedDownKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onDownKey.bind(this));
673349
+ events2.numberKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onNumberKey.bind(this));
673350
+ events2.spaceKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onSpaceKey.bind(this));
673351
+ events2.aKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onAllKey.bind(this));
673352
+ events2.iKey.pipe((0, import_rxjs8.takeUntil)(validation.success)).forEach(this.onInverseKey.bind(this));
673353
673353
  this.render();
673354
673354
  this.firstRender = false;
673355
673355
  return this;
@@ -673502,12 +673502,12 @@ var PasswordPrompt = class extends Prompt {
673502
673502
  */
673503
673503
  _run(cb) {
673504
673504
  this.done = cb;
673505
- const events = observe(this.rl);
673506
- const submit = events.line.pipe((0, import_rxjs9.map)(this.filterInput.bind(this)));
673505
+ const events2 = observe(this.rl);
673506
+ const submit = events2.line.pipe((0, import_rxjs9.map)(this.filterInput.bind(this)));
673507
673507
  const validation = this.handleSubmitEvents(submit);
673508
673508
  validation.success.forEach(this.onEnd.bind(this));
673509
673509
  validation.error.forEach(this.onError.bind(this));
673510
- events.keypress.pipe((0, import_rxjs9.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673510
+ events2.keypress.pipe((0, import_rxjs9.takeUntil)(validation.success)).forEach(this.onKeypress.bind(this));
673511
673511
  this.render();
673512
673512
  return this;
673513
673513
  }
@@ -673774,8 +673774,8 @@ var EditorPrompt = class extends Prompt {
673774
673774
  _run(cb) {
673775
673775
  this.done = cb;
673776
673776
  this.editorResult = new import_rxjs10.Subject();
673777
- const events = observe(this.rl);
673778
- this.lineSubscription = events.line.subscribe(this.startExternalEditor.bind(this));
673777
+ const events2 = observe(this.rl);
673778
+ this.lineSubscription = events2.line.subscribe(this.startExternalEditor.bind(this));
673779
673779
  const waitUserInput = this.opt.waitUserInput === void 0 ? true : this.opt.waitUserInput;
673780
673780
  const validation = this.handleSubmitEvents(this.editorResult);
673781
673781
  validation.success.forEach(this.onEnd.bind(this));
@@ -730850,7 +730850,7 @@ var DeviceAuthService = class {
730850
730850
  * Honors `slow_down` by widening the interval (RFC 8628 §3.5).
730851
730851
  */
730852
730852
  async pollUntilDone(deviceCode, opts) {
730853
- const sleep = opts.sleep || defaultSleep;
730853
+ const sleep2 = opts.sleep || defaultSleep;
730854
730854
  let intervalMs = Math.max(1, opts.interval) * 1e3;
730855
730855
  const caps = [Date.now() + opts.expiresIn * 1e3];
730856
730856
  if (opts.timeoutMs != null) caps.push(Date.now() + opts.timeoutMs);
@@ -730880,7 +730880,7 @@ var DeviceAuthService = class {
730880
730880
  "Timed out waiting for device approval. Run `hubdoc login --start` again."
730881
730881
  );
730882
730882
  }
730883
- await sleep(intervalMs);
730883
+ await sleep2(intervalMs);
730884
730884
  }
730885
730885
  }
730886
730886
  };
@@ -733822,7 +733822,7 @@ var PERM_COLUMNS = [
733822
733822
  { header: "ID", key: "id" },
733823
733823
  { header: "Actor", key: "actor_type", format: (v6, row) => `${v6}:${row.actor?.display_name ?? row.actor_id?.substring(0, 8)}` },
733824
733824
  { header: "Level", key: "level" },
733825
- { header: "On", key: "permissible_type", format: (v6) => v6?.replace(/^Documents::/, "") ?? "" },
733825
+ { header: "On", key: "permissible_type", format: (v6) => v6?.replace(/^(Documents|Projects)::/, "") ?? "" },
733826
733826
  { header: "Resource ID", key: "permissible_id" },
733827
733827
  { header: "Updated", key: "updated_at" }
733828
733828
  ];
@@ -733831,7 +733831,8 @@ var PERMISSIBLE_ALIASES = {
733831
733831
  workspace: "Documents::Workspace",
733832
733832
  folder: "Documents::Folder",
733833
733833
  file: "Documents::File",
733834
- share_link: "Documents::ShareLink"
733834
+ share_link: "Documents::ShareLink",
733835
+ project: "Projects::Project"
733835
733836
  };
733836
733837
  var ACTOR_ALIASES = {
733837
733838
  user: "User",
@@ -733841,10 +733842,10 @@ var ACTOR_ALIASES = {
733841
733842
  function normalizePermissibleType(input, out) {
733842
733843
  const lower = input.toLowerCase();
733843
733844
  if (PERMISSIBLE_ALIASES[lower]) return PERMISSIBLE_ALIASES[lower];
733844
- if (input.startsWith("Documents::")) return input;
733845
+ if (input.startsWith("Documents::") || input.startsWith("Projects::")) return input;
733845
733846
  out.fail(
733846
733847
  new Error(
733847
- `Invalid --on-type: '${input}'. Use workspace/folder/file/share_link or a fully-qualified Documents::\u2026 class name.`
733848
+ `Invalid --on-type: '${input}'. Use workspace/folder/file/share_link/project or a fully-qualified Documents::\u2026 / Projects::\u2026 class name.`
733848
733849
  )
733849
733850
  );
733850
733851
  return "";
@@ -734334,7 +734335,7 @@ async function handleTicketsShow(id, opts) {
734334
734335
  out.fail(err);
734335
734336
  }
734336
734337
  }
734337
- function ticketBodyFromOpts(opts, includeAll) {
734338
+ function ticketBodyFromOpts(opts, out) {
734338
734339
  const body2 = {};
734339
734340
  if (opts.title !== void 0) body2.title = opts.title;
734340
734341
  if (opts.description !== void 0) body2.description = opts.description;
@@ -734355,14 +734356,21 @@ function ticketBodyFromOpts(opts, includeAll) {
734355
734356
  const labels = parseLabels(opts.labels);
734356
734357
  if (labels !== void 0) body2.labels = labels;
734357
734358
  if (opts.column !== void 0) body2.column_status = opts.column;
734358
- return includeAll ? body2 : body2;
734359
+ if (opts.agentConfig !== void 0) {
734360
+ try {
734361
+ body2.agent_config = JSON.parse(opts.agentConfig);
734362
+ } catch (err) {
734363
+ out.fail(new Error(`Invalid JSON for --agent-config: ${err.message}`));
734364
+ }
734365
+ }
734366
+ return body2;
734359
734367
  }
734360
734368
  async function handleTicketsCreate(opts) {
734361
734369
  const out = output(opts);
734362
734370
  requireProject2(opts, out);
734363
734371
  const ctx = await buildApiContext(out);
734364
734372
  if (!opts.title) out.fail(new Error("--title is required"));
734365
- const body2 = ticketBodyFromOpts(opts, true);
734373
+ const body2 = ticketBodyFromOpts(opts, out);
734366
734374
  try {
734367
734375
  const res = await ctx.axios.post(ticketsBase(opts.project), body2);
734368
734376
  const ticket = res.data?.ticket ?? res.data;
@@ -734375,7 +734383,7 @@ async function handleTicketsUpdate(id, opts) {
734375
734383
  const out = output(opts);
734376
734384
  requireProject2(opts, out);
734377
734385
  const ctx = await buildApiContext(out);
734378
- const body2 = ticketBodyFromOpts(opts, true);
734386
+ const body2 = ticketBodyFromOpts(opts, out);
734379
734387
  if (Object.keys(body2).length === 0) out.fail(new Error("No fields to update"));
734380
734388
  try {
734381
734389
  const res = await ctx.axios.patch(
@@ -734414,6 +734422,34 @@ async function handleTicketsDelete(id, opts) {
734414
734422
  out.fail(err);
734415
734423
  }
734416
734424
  }
734425
+ async function handleTicketsSetAgent(id, opts) {
734426
+ const out = output(opts);
734427
+ requireProject2(opts, out);
734428
+ const ctx = await buildApiContext(out);
734429
+ if (opts.agent === void 0) out.fail(new Error("--agent <agent-id|null> is required"));
734430
+ const body2 = {
734431
+ assignee_id: opts.agent === "null" ? null : opts.agent
734432
+ };
734433
+ if (opts.agentConfig !== void 0) {
734434
+ try {
734435
+ body2.agent_config = JSON.parse(opts.agentConfig);
734436
+ } catch (err) {
734437
+ out.fail(new Error(`Invalid JSON for --agent-config: ${err.message}`));
734438
+ }
734439
+ }
734440
+ try {
734441
+ const res = await ctx.axios.patch(
734442
+ `${ticketsBase(opts.project)}/${encodeURIComponent(id)}`,
734443
+ body2
734444
+ );
734445
+ out.ok(
734446
+ body2.assignee_id === null ? `Ticket ${id} agent cleared` : `Ticket ${id} agent set to ${body2.assignee_id}`,
734447
+ res.data?.ticket ?? res.data
734448
+ );
734449
+ } catch (err) {
734450
+ out.fail(err);
734451
+ }
734452
+ }
734417
734453
  async function handleTicketsAssign(id, opts) {
734418
734454
  const out = output(opts);
734419
734455
  requireProject2(opts, out);
@@ -734604,6 +734640,179 @@ async function handleTicketsDispatch(id, opts) {
734604
734640
  }
734605
734641
  }
734606
734642
 
734643
+ // apps/cli/cli/handlers/agent-handlers.ts
734644
+ var BASE4 = "/api/v1/agents";
734645
+ var AGENT_COLUMNS = [
734646
+ { header: "ID", key: "id" },
734647
+ { header: "Key", key: "key" },
734648
+ { header: "Name", key: "name", maxWidth: 30 },
734649
+ { header: "Type", key: "type", format: (v6) => v6?.replace(/^Agents::/, "") ?? "" },
734650
+ { header: "Active", key: "active", format: (v6) => v6 ? "\u2714" : "" }
734651
+ ];
734652
+ async function handleAgentsList(opts) {
734653
+ const out = output(opts);
734654
+ const ctx = await buildApiContext(out);
734655
+ const params = {};
734656
+ if (opts.active !== void 0) params.active = opts.active;
734657
+ if (opts.page) params.page = Number(opts.page);
734658
+ if (opts.perPage) params.per_page = Number(opts.perPage);
734659
+ try {
734660
+ const res = await ctx.axios.get(BASE4, { params });
734661
+ const items = Array.isArray(res.data) ? res.data : [];
734662
+ const pagination = extractPagination(res.headers, items.length);
734663
+ if (opts.json) {
734664
+ out.show({ items, pagination });
734665
+ return;
734666
+ }
734667
+ out.list(items, AGENT_COLUMNS);
734668
+ if (pagination.pages > 1) {
734669
+ out.note(`page ${pagination.page}/${pagination.pages} (${pagination.count} total)`);
734670
+ }
734671
+ } catch (err) {
734672
+ out.fail(err);
734673
+ }
734674
+ }
734675
+ async function handleAgentsShow(id, opts) {
734676
+ const out = output(opts);
734677
+ const ctx = await buildApiContext(out);
734678
+ try {
734679
+ const res = await ctx.axios.get(`${BASE4}/${encodeURIComponent(id)}`);
734680
+ out.show(res.data?.agent ?? res.data, { title: `Agent ${id}` });
734681
+ } catch (err) {
734682
+ out.fail(err);
734683
+ }
734684
+ }
734685
+
734686
+ // apps/cli/cli/handlers/event-handlers.ts
734687
+ var BASE5 = "/api/v1/events";
734688
+ var EVENT_COLUMNS = [
734689
+ { header: "Event ID", key: "event_id" },
734690
+ { header: "Type", key: "type", format: (v6) => shortType(v6) },
734691
+ { header: "At", key: "timestamp" }
734692
+ ];
734693
+ var TYPE_ALIASES = {
734694
+ // Projects
734695
+ "project.created": "Projects::Projects::ProjectCreated",
734696
+ "project.updated": "Projects::Projects::ProjectUpdated",
734697
+ "project.started": "Projects::Projects::ProjectStarted",
734698
+ "project.completed": "Projects::Projects::ProjectCompleted",
734699
+ "project.archived": "Projects::Projects::ProjectArchived",
734700
+ "project.deleted": "Projects::Projects::ProjectDeleted",
734701
+ // Sprints
734702
+ "sprint.created": "Projects::Sprints::SprintCreated",
734703
+ "sprint.updated": "Projects::Sprints::SprintUpdated",
734704
+ "sprint.started": "Projects::Sprints::SprintStarted",
734705
+ "sprint.completed": "Projects::Sprints::SprintCompleted",
734706
+ "sprint.deleted": "Projects::Sprints::SprintDeleted",
734707
+ // Tickets
734708
+ "ticket.created": "Projects::Tickets::TicketCreated",
734709
+ "ticket.updated": "Projects::Tickets::TicketUpdated",
734710
+ "ticket.assigned": "Projects::Tickets::TicketAssigned",
734711
+ "ticket.blocked": "Projects::Tickets::TicketBlocked",
734712
+ "ticket.escalated": "Projects::Tickets::TicketEscalated",
734713
+ "ticket.failed": "Projects::Tickets::TicketFailed",
734714
+ "ticket.moved": "Projects::Tickets::TicketMoved",
734715
+ "ticket.status_changed": "Projects::Tickets::TicketStatusChanged",
734716
+ "ticket.deleted": "Projects::Tickets::TicketDeleted",
734717
+ "ticket.comment": "Projects::Tickets::ChannelMessagePosted",
734718
+ "ticket.deliverable": "Projects::Tickets::DeliverableAttached",
734719
+ // Tasks
734720
+ "task.created": "Projects::Tasks::TaskCreated",
734721
+ "task.updated": "Projects::Tasks::TaskUpdated",
734722
+ "task.completed": "Projects::Tasks::TaskCompleted",
734723
+ "task.deleted": "Projects::Tasks::TaskDeleted"
734724
+ };
734725
+ function expandTypes(raw) {
734726
+ if (raw == null) return void 0;
734727
+ const items = raw.split(",").map((s3) => s3.trim()).filter(Boolean).map((s3) => TYPE_ALIASES[s3] ?? s3);
734728
+ return items.length > 0 ? items.join(",") : void 0;
734729
+ }
734730
+ function shortType(fq) {
734731
+ if (!fq) return "";
734732
+ const inverted = Object.entries(TYPE_ALIASES).find(([, v6]) => v6 === fq)?.[0];
734733
+ if (inverted) return inverted;
734734
+ return fq.split("::").pop() ?? fq;
734735
+ }
734736
+ async function fetchEventsPage(ctx, opts) {
734737
+ const params = {};
734738
+ if (opts.project) params.project_id = opts.project;
734739
+ if (opts.ticket) params.ticket_id = opts.ticket;
734740
+ const expanded = expandTypes(opts.type);
734741
+ if (expanded) params.type = expanded;
734742
+ if (opts.since) params.since = opts.since;
734743
+ if (opts.limit) params.limit = Number(opts.limit);
734744
+ const res = await ctx.axios.get(BASE5, { params });
734745
+ const items = Array.isArray(res.data) ? res.data : [];
734746
+ const headers = res.headers ?? {};
734747
+ const nextCursor = headers["x-next-cursor"] ?? headers["X-Next-Cursor"] ?? null;
734748
+ return { items, nextCursor };
734749
+ }
734750
+ async function handleEventsList(opts) {
734751
+ const out = output(opts);
734752
+ const ctx = await buildApiContext(out);
734753
+ try {
734754
+ const { items, nextCursor } = await fetchEventsPage(ctx, opts);
734755
+ if (opts.json) {
734756
+ out.show({ items, next_cursor: nextCursor });
734757
+ return;
734758
+ }
734759
+ out.list(items, EVENT_COLUMNS);
734760
+ if (nextCursor) {
734761
+ out.note(`next_cursor: ${nextCursor} (pass to --since to continue)`);
734762
+ }
734763
+ } catch (err) {
734764
+ out.fail(err);
734765
+ }
734766
+ }
734767
+ async function handleEventsWatch(opts) {
734768
+ const out = output(opts);
734769
+ const ctx = await buildApiContext(out);
734770
+ const timeoutMs = Math.max(1e3, Number(opts.timeout ?? "60") * 1e3);
734771
+ const intervalMs = Math.max(100, Number(opts.pollIntervalMs ?? "1000"));
734772
+ const startedAt = Date.now();
734773
+ let cursor2 = opts.since;
734774
+ try {
734775
+ while (Date.now() - startedAt < timeoutMs) {
734776
+ const page = await fetchEventsPage(ctx, { ...opts, since: cursor2 });
734777
+ if (page.items.length > 0) {
734778
+ if (opts.json) {
734779
+ out.show({
734780
+ items: page.items,
734781
+ next_cursor: page.nextCursor,
734782
+ waited_ms: Date.now() - startedAt
734783
+ });
734784
+ } else {
734785
+ out.list(page.items, EVENT_COLUMNS);
734786
+ if (page.nextCursor) {
734787
+ out.note(`next_cursor: ${page.nextCursor}`);
734788
+ }
734789
+ }
734790
+ return;
734791
+ }
734792
+ await sleep(intervalMs);
734793
+ }
734794
+ } catch (err) {
734795
+ out.fail(err);
734796
+ }
734797
+ const waited = Date.now() - startedAt;
734798
+ const payload2 = {
734799
+ ok: false,
734800
+ reason: "timeout",
734801
+ waited_ms: waited,
734802
+ since: cursor2 ?? null
734803
+ };
734804
+ if (opts.json) {
734805
+ process.stderr.write(JSON.stringify(payload2, null, 2) + "\n");
734806
+ } else {
734807
+ process.stderr.write(`Timed out after ${waited}ms with no matching event.
734808
+ `);
734809
+ }
734810
+ process.exit(2);
734811
+ }
734812
+ function sleep(ms) {
734813
+ return new Promise((resolve) => setTimeout(resolve, ms));
734814
+ }
734815
+
734607
734816
  // apps/cli/cli/handlers/task-handlers.ts
734608
734817
  var tasksBase = (pid, tid) => `/api/v1/projects/${encodeURIComponent(pid)}/tickets/${encodeURIComponent(tid)}/tasks`;
734609
734818
  var TASK_COLUMNS = [
@@ -734778,7 +734987,7 @@ async function handleTasksReorder(opts) {
734778
734987
  // apps/cli/cli.ts
734779
734988
  var getVersion = () => {
734780
734989
  try {
734781
- if (true) return "1.10.0";
734990
+ if (true) return "1.12.0";
734782
734991
  } catch {
734783
734992
  }
734784
734993
  for (const candidate of [
@@ -735060,13 +735269,13 @@ members.command("remove <membership-id>").description("Remove a membership by it
735060
735269
  await handleGroupMembersRemove(id, withGlobals(options));
735061
735270
  });
735062
735271
  var permissions = program2.command("permissions").description("Manage ACL: grant / list / revoke permissions on documents resources");
735063
- permissions.command("ls").description("List permissions on a specific resource").requiredOption("--on <resource-id>", "Permissible resource UUID").option("--on-type <type>", "workspace | folder | file | share_link", "workspace").option("--to <actor-id>", "Filter by actor id (client-side)").option("--to-type <type>", "Filter by actor type (user | group | contact)").option("--level <level>", "Filter by level (read|write|admin|owner|forbidden)").action(async (options) => {
735272
+ permissions.command("ls").description("List permissions on a specific resource").requiredOption("--on <resource-id>", "Permissible resource UUID").option("--on-type <type>", "workspace | folder | file | share_link | project", "workspace").option("--to <actor-id>", "Filter by actor id (client-side)").option("--to-type <type>", "Filter by actor type (user | group | contact)").option("--level <level>", "Filter by level (read|write|admin|owner|forbidden)").action(async (options) => {
735064
735273
  await handlePermissionsList(withGlobals(options));
735065
735274
  });
735066
735275
  permissions.command("show <id>").description("Show a permission by id").action(async (id, options) => {
735067
735276
  await handlePermissionsShow(id, withGlobals(options));
735068
735277
  });
735069
- permissions.command("grant").description("Grant a permission on a resource to a user or group").requiredOption("--to <actor-id>", "Actor UUID (user/group/contact)").requiredOption("--to-type <type>", "user | group | contact").requiredOption("--on <resource-id>", "Permissible resource UUID").requiredOption("--on-type <type>", "workspace | folder | file | share_link").requiredOption("--level <level>", "read | write | admin | owner | forbidden").option("--temporary", "Mark as temporary (auto-expiring per server policy)").action(async (options) => {
735278
+ permissions.command("grant").description("Grant a permission on a resource to a user or group").requiredOption("--to <actor-id>", "Actor UUID (user/group/contact)").requiredOption("--to-type <type>", "user | group | contact").requiredOption("--on <resource-id>", "Permissible resource UUID").requiredOption("--on-type <type>", "workspace | folder | file | share_link | project").requiredOption("--level <level>", "read | write | admin | owner | forbidden").option("--temporary", "Mark as temporary (auto-expiring per server policy)").action(async (options) => {
735070
735279
  await handlePermissionsGrant(withGlobals(options));
735071
735280
  });
735072
735281
  permissions.command("revoke <id>").description("Revoke a permission by id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
@@ -735123,10 +735332,10 @@ tickets.command("ls").alias("list").description("List tickets").requiredOption("
735123
735332
  tickets.command("show <id>").description("Show a ticket (with tasks, subtasks, deliverables)").requiredOption("--project <id>", "Parent project id").action(async (id, options) => {
735124
735333
  await handleTicketsShow(id, withGlobals(options));
735125
735334
  });
735126
- tickets.command("create").description("Create a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--title <text>").option("--description <text>").option("--type <ticket_type>", "task (default) | bug | story | epic | \u2026").option("--sprint <id>", "Attach to a sprint; omit to leave in backlog").option("--assignee <id>", "Assign to a user id").option("--priority <priority>", "lowest | low | medium | high | highest").option("--status <status>").option("--parent <id>", "Parent ticket id (for subtasks)").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>", "Comma-separated labels").option("--column <column_status>").action(async (options) => {
735335
+ tickets.command("create").description("Create a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--title <text>").option("--description <text>").option("--type <ticket_type>", "task (default) | bug | story | epic | \u2026").option("--sprint <id>", "Attach to a sprint; omit to leave in backlog").option("--assignee <id>", "Assign to a user id").option("--priority <priority>", "lowest | low | medium | high | highest").option("--status <status>").option("--parent <id>", "Parent ticket id (for subtasks)").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>", "Comma-separated labels").option("--column <column_status>").option("--agent-config <json>", "JSON payload written to the ticket agent_config (corex#591)").action(async (options) => {
735127
735336
  await handleTicketsCreate(withGlobals(options));
735128
735337
  });
735129
- tickets.command("update <id>").description("Update a ticket").requiredOption("--project <id>", "Parent project id").option("--title <text>").option("--description <text>").option("--type <ticket_type>").option("--sprint <id>", 'Move to a sprint; use "null" for backlog').option("--assignee <id>", 'Reassign; use "null" to unassign').option("--priority <priority>").option("--status <status>").option("--parent <id>").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>").option("--column <column_status>").action(async (id, options) => {
735338
+ tickets.command("update <id>").description("Update a ticket").requiredOption("--project <id>", "Parent project id").option("--title <text>").option("--description <text>").option("--type <ticket_type>").option("--sprint <id>", 'Move to a sprint; use "null" for backlog').option("--assignee <id>", 'Reassign; use "null" to unassign').option("--priority <priority>").option("--status <status>").option("--parent <id>").option("--due <YYYY-MM-DD>").option("--story-points <n>").option("--labels <a,b,c>").option("--column <column_status>").option("--agent-config <json>", "JSON payload written to the ticket agent_config (corex#591)").action(async (id, options) => {
735130
735339
  await handleTicketsUpdate(id, withGlobals(options));
735131
735340
  });
735132
735341
  tickets.command("delete <id>").description("Delete a ticket (drops its tasks, deliverables, activities)").requiredOption("--project <id>", "Parent project id").option("--yes", "Skip the interactive confirmation").action(async (id, options) => {
@@ -735135,6 +735344,9 @@ tickets.command("delete <id>").description("Delete a ticket (drops its tasks, de
735135
735344
  tickets.command("assign <id>").description("Assign a ticket to a user; --to null to unassign").requiredOption("--project <id>", "Parent project id").requiredOption("--to <user-id|null>").action(async (id, options) => {
735136
735345
  await handleTicketsAssign(id, withGlobals(options));
735137
735346
  });
735347
+ tickets.command("set-agent <id>").description("Configure the ticket with an agent (assignee + agent_config in one PATCH) \u2014 needed before `dispatch` (corex#591)").requiredOption("--project <id>", "Parent project id").requiredOption("--agent <agent-id|null>", 'Agents::Agent UUID (use "null" to clear)').option("--agent-config <json>", "Optional JSON payload merged into agent_config").action(async (id, options) => {
735348
+ await handleTicketsSetAgent(id, withGlobals(options));
735349
+ });
735138
735350
  tickets.command("status <id>").description("Change a ticket status (optionally pin the kanban column with --column)").requiredOption("--project <id>", "Parent project id").requiredOption("--to <status>", "open | in_progress | done | blocked | awaiting_human | cancelled | failed").option("--column <column_status>", "Optionally set the kanban column at the same time").action(async (id, options) => {
735139
735351
  await handleTicketsStatus(id, withGlobals(options));
735140
735352
  });
@@ -735178,6 +735390,20 @@ tasks.command("complete <id>").description("Mark a task as completed (or re-open
735178
735390
  tasks.command("reorder").description("Reorder tasks under a ticket").requiredOption("--project <id>", "Parent project id").requiredOption("--ticket <id>", "Parent ticket id").requiredOption("--order <ids>", "Comma-separated task ids in the desired order").action(async (options) => {
735179
735391
  await handleTasksReorder(withGlobals(options));
735180
735392
  });
735393
+ var agents = program2.command("agents").description("List agents dispatchable on tickets (corex#591)");
735394
+ agents.command("ls").alias("list").description("List agents").option("--active", "Only agents whose config is active").option("--page <n>").option("--per-page <n>").action(async (options) => {
735395
+ await handleAgentsList(withGlobals(options));
735396
+ });
735397
+ agents.command("show <id>").description("Show an agent").action(async (id, options) => {
735398
+ await handleAgentsShow(id, withGlobals(options));
735399
+ });
735400
+ var events = program2.command("events").description("Read the Rails Event Store stream (corex#593)");
735401
+ events.command("ls").alias("list").description("One-shot cursor-paginated read of the event stream").option("--project <id>", "Filter to events touching a specific project").option("--ticket <id>", "Filter to events on a specific ticket").option("--type <a,b,c>", "Comma-separated event types (aliases like ticket.status_changed OR full RES class names)").option("--since <cursor>", "Read events strictly after this event_id").option("--limit <n>", "Max events (default 100, server-side max 1000)").action(async (options) => {
735402
+ await handleEventsList(withGlobals(options));
735403
+ });
735404
+ events.command("watch").description("Block until a matching event arrives or the timeout expires (client-side long-poll)").option("--project <id>", "Filter to events touching a specific project").option("--ticket <id>", "Filter to events on a specific ticket").option("--type <a,b,c>", "Comma-separated event types (aliases or full class names)").option("--since <cursor>", "Start watching from this event_id (exclusive)").option("--limit <n>", "Max events per poll (default 100)").option("--timeout <s>", "Give up after this many seconds (default 60, exit 2 on timeout)").option("--poll-interval-ms <ms>", "Delay between polls (default 1000, min 100)").action(async (options) => {
735405
+ await handleEventsWatch(withGlobals(options));
735406
+ });
735181
735407
  program2.parse();
735182
735408
  /*! Bundled license information:
735183
735409
 
package/docs/llm-usage.md CHANGED
@@ -377,16 +377,78 @@ themselves. All commands support `--json` for machine parsing.
377
377
  | start | complete`.
378
378
 
379
379
  `hubdoc tickets --project=<pid>` — `ls | show | create | update | delete
380
- | assign | status | move | activities | deliverable | dispatch |
381
- comment | comments`. Every verb requires `--project=<pid>`. `ls
382
- --backlog` (or `ls --sprint null`) lists the backlog. `assign --to null`
383
- unassigns. `status --to <state> [--column <col>]` uses the canonical
384
- statuses: `open | in_progress | done | blocked | awaiting_human |
385
- cancelled | failed` and optionally pins the kanban column at the same
386
- time. `comment <id> --body <text>` (or `--body-file <path>`) posts a
387
- message on the ticket's discussion thread (visible in Corex / Agoria,
388
- attributed to the token holder); `comments <id>` reads the thread
389
- oldest-first (paginated like `activities`).
380
+ | assign | set-agent | status | move | activities | deliverable |
381
+ dispatch | comment | comments`. Every verb requires `--project=<pid>`.
382
+ `ls --backlog` (or `ls --sprint null`) lists the backlog. `assign --to
383
+ null` unassigns. `status --to <state> [--column <col>]` uses the
384
+ canonical statuses: `open | in_progress | done | blocked |
385
+ awaiting_human | cancelled | failed` and optionally pins the kanban
386
+ column at the same time. `comment <id> --body <text>` (or `--body-file
387
+ <path>`) posts a message on the ticket's discussion thread (visible in
388
+ Corex / Agoria, attributed to the token holder); `comments <id>` reads
389
+ the thread oldest-first (paginated like `activities`).
390
+
391
+ **Agent configuration on tickets** (corex#591) : `create` and `update`
392
+ both accept `--agent-config <json>` — free-form jsonb persisted on the
393
+ ticket and read back by `dispatch_agent` / the agent runner (canonical
394
+ keys: `agent_role: planner|executor|…`, `instructions`). The convenience
395
+ verb `tickets set-agent <id> --project <pid> --agent <agent-id>
396
+ [--agent-config <json>]` PATCHes `assignee_id` and (optionally)
397
+ `agent_config` in one call — the shortest path from "empty ticket" to
398
+ "dispatchable". Use `hubdoc agents ls [--active]` to discover
399
+ dispatchable agent UUIDs (STI: `Agents::Planner`, `Agents::Executor`,
400
+ `Agents::Classifier`, `Agents::Conversational`, …).
401
+
402
+ **Project ACL** (hubdoc-tools#15) : the `permissions` verbs now accept
403
+ `--on-type project` (alias of `Projects::Project`) — the corex
404
+ `/api/v1/permissions` controller is polymorphic and `Projects::Project`
405
+ includes `HasPermissionsConcern`. This lets an agent share a **standalone
406
+ project** (no workspace attached) with a colleague or another agent
407
+ without going through a workspace-scoped ACL. Example:
408
+ `hubdoc permissions grant --to <user-id> --to-type user --on <project-id>
409
+ --on-type project --level admin`.
410
+
411
+ ### Events — react to what's happening (corex#593)
412
+
413
+ `hubdoc events ls` and `hubdoc events watch` sit on top of the Rails
414
+ Event Store stream. Every mutation done by `projects/sprints/tickets/
415
+ tasks` (create, update, status_changed, moved, deliverable_attached,
416
+ channel_message_posted, …) is published as a typed event; the CLI lets
417
+ an agent either **poll** the stream once (`ls`) or **block** until
418
+ something happens (`watch`).
419
+
420
+ - **Type aliases** (agent-friendly): `ticket.created | ticket.updated |
421
+ ticket.status_changed | ticket.assigned | ticket.blocked |
422
+ ticket.escalated | ticket.failed | ticket.moved | ticket.comment |
423
+ ticket.deliverable | ticket.deleted | sprint.{created,started,updated,
424
+ completed,deleted} | project.{created,updated,started,completed,
425
+ archived,deleted} | task.{created,updated,completed,deleted}`. Full RES
426
+ class names (`Projects::Tickets::TicketStatusChanged`) are also
427
+ accepted verbatim.
428
+ - **Cursor**: each `ls` response includes `next_cursor` — pass it as
429
+ `--since` on the next call to resume. `watch` re-uses the same `since`
430
+ across empty polls (never skips an event that arrives mid-poll).
431
+ - **Timeout**: `watch --timeout <s>` (default 60) — exit code 2 with a
432
+ `{ ok:false, reason:"timeout", waited_ms, since }` envelope on stderr
433
+ when the window expires. This is distinct from network errors (exit 1).
434
+ - **Poll cadence**: `watch --poll-interval-ms <n>` (default 1000, min
435
+ 100). Kept short so the perceived latency stays low; harmless because
436
+ each poll is a lightweight bare-array read.
437
+
438
+ Two typical patterns:
439
+
440
+ ```bash
441
+ # 1) Wait for the dispatched agent to finish before moving on.
442
+ hubdoc events watch --project "$PID" --ticket "$TID" \
443
+ --type ticket.status_changed --timeout 900 --json \
444
+ | jq -r '.items[-1].data.new_status' # → done / failed / blocked
445
+ # (returns as soon as the first matching event arrives)
446
+
447
+ # 2) Loop over the day's activity from a saved checkpoint.
448
+ CURSOR=$(cat .last-cursor 2>/dev/null || echo "")
449
+ hubdoc events ls --project "$PID" --since "$CURSOR" --limit 200 --json \
450
+ | tee day-batch.json | jq -r '.next_cursor' > .last-cursor
451
+ ```
390
452
 
391
453
  `hubdoc tasks --project=<pid> --ticket=<tid>` — `ls | show | create |
392
454
  update | delete | complete | reorder`. Sub-tasks live under a ticket;
@@ -444,7 +506,13 @@ hubdoc tasks create --project "$PID" --ticket "$TID" \
444
506
  hubdoc tasks create --project "$PID" --ticket "$TID" \
445
507
  --title "Rédiger devis" --priority high --due 2026-07-16 --json
446
508
 
447
- # 5. Dispatch the agent already configured on the ticket.
509
+ # 5. Pick a dispatchable agent then set it on the ticket in one shot.
510
+ AGENT_ID=$(hubdoc agents ls --active --json | jq -r '.items[] | select(.key=="planner") | .id')
511
+ hubdoc tickets set-agent "$TID" --project "$PID" \
512
+ --agent "$AGENT_ID" \
513
+ --agent-config '{"agent_role":"planner","instructions":"Rédige un devis à partir des PJ."}' --json
514
+
515
+ # 5b. Now `dispatch` can actually fire (agent + agent_config both set).
448
516
  hubdoc tickets dispatch "$TID" --project "$PID" --json
449
517
 
450
518
  # 6. Later — post a follow-up on the ticket thread (visible to the human):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinoia/hubdoc-tools",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Professional command-line tool for HubDoc document management and bulk import/export",
5
5
  "main": "cli.js",
6
6
  "bin": {